pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedPluggable storage
138+ messages / 23 participants
[nested] [flat]
* Pluggable storage
@ 2016-08-12 23:15 Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 11:55 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2016-08-15 18:09 ` Re: Pluggable storage Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-17 13:01 ` Re: Pluggable storage Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 5 replies; 138+ messages in thread
From: Alvaro Herrera @ 2016-08-12 23:15 UTC (permalink / raw)
To: pgsql-hackers
Many have expressed their interest in this topic, but I haven't seen any
design of how it should work. Here's my attempt; I've been playing with
this for some time now and I think what I propose here is a good initial
plan. This will allow us to write permanent table storage that works
differently than heapam.c. At this stage, I haven't throught through
whether this is going to allow extensions to define new storage modules;
I am focusing on AMs that can coexist with heapam in core.
The design starts with a new row type in pg_am, of type "s" (for "storage").
The handler function returns a struct of node StorageAmRoutine. This
contains functions for 1) scans (beginscan, getnext, endscan) 2) tuples
(tuple_insert/update/delete/lock, as well as set_oid, get_xmin and the
like), and operations on tuples that are part of slots (tuple_deform,
materialize).
To support this, we introduce StorageTuple and StorageScanDesc.
StorageTuples represent a physical tuple coming from some storage AM.
It is necessary to have a pointer to a StorageAmRoutine in order to
manipulate the tuple. For heapam.c, a StorageTuple is just a HeapTuple.
RelationData gains ->rd_stamroutine which is a pointer to the
StorageAmRoutine for the relation in question. Similarly,
TupleTableSlot is augmented with a link to the StorageAmRoutine to
handle the StorageTuple it contains (probably in most cases it's set at
the same time as the tupdesc). This implies that routines such as
ExecAssignScanType need to pass down the StorageAmRoutine from the
relation to the slot.
The executor is modified so that instead of calling heap_insert etc
directly, it uses rel->rd_stamroutine to call these methods. The
executor is still in charge of dealing with indexes, constraints, and
any other thing that's not the tuple storage itself (this is one major
point in which this differs from FDWs). This all looks simple enough,
with one exception and a few notes:
exception a) ExecMaterializeSlot needs special consideration. This is
used in two different ways: a1) is the stated "make tuple independent
from any underlying storage" point, which is handled by
ExecMaterializeSlot itself and calling a method from the storage AM to
do any byte copying as needed. ExecMaterializeSlot no longer returns a
HeapTuple, because there might not be any. The second usage pattern a2)
is to create a HeapTuple that's passed to other modules which only deal
with HT and not slots (triggers are the main case I noticed, but I think
there are others such as the executor itself wanting tuples as Datum for
some reason). For the moment I'm handling this by having a new
ExecHeapifyTuple which creates a HeapTuple from a slot, regardless of
the original tuple format.
note b) EvalPlanQual currently maintains an array of HeapTuple in
EState->es_epqTuple. I think it works to replace that with an array of
StorageTuples; EvalPlanQualFetch needs to call the StorageAmRoutine
methods in order to interact with it. Other than those changes, it
seems okay.
note c) nodeSubplan has curTuple as a HeapTuple. It seems simple
to replace this with an independent slot-based tuple.
note d) grp_firstTuple in nodeAgg / nodeSetOp. These are less
simple than the above, but replacing the HeapTuple with a slot-based
tuple seems doable too.
note e) nodeLockRows uses lr_curtuples to feed EvalPlanQual.
TupleTableSlot also seems a good replacement. This has fallout in other
users of EvalPlanQual, too.
note f) More widespread, MinimalTuples currently use a tweaked HeapTuple
format. In the long run, it may be possible to replace them with a
separate storage module that's specifically designed to handle tuples
meant for tuplestores etc. That may simplify TupleTableSlot and
execTuples. For the moment we keep the tts_mintuple as it is. Whenever
a tuple is not already in heap format, we heapify it in order to put in
the store.
The current heapam.c routines need some changes. Currently, practice is
that heap_insert, heap_multi_insert, heap_fetch, heap_update scribble on
their input tuples to set the resulting ItemPointer in tuple->t_self.
This is messy if we want StorageTuples to be abstract. I'm changing
this so that the resulting ItemPointer is returned in a separate output
argument; the tuple itself is left alone. This is somewhat messy in the
case of heap_multi_insert because it returns several items; I think it's
acceptable to return an array of ItemPointers in the same order as the
input tuples. This works fine for the only caller, which is COPY in
batch mode. For the other routines, they don't really care where the
TID is returned AFAICS.
Additional noteworthy items:
i) Speculative insertion: the speculative insertion token is no longer
installed directly in the heap tuple by the executor (of course).
Instead, the token becomes part of the slot. When the tuple_insert
method is called, the insertion routine is in charge of setting the
token from the slot into the storage tuple. Executor is in charge of
calling method->speculative_finish() / abort() once the insertion has
been confirmed by the indexes.
ii) execTuples has additional accessors for tuples-in-slot, such as
ExecFetchSlotTuple and friends. I expect to have some of them to return
abstract StorageTuples, others HeapTuple or MinimalTuples (possibly
wrapped in Datum), depending on callers. We might be able to cut down
on these later; my first cut will try to avoid API changes to keep
fallout to a minimum.
iii) All tuples need to be identifiable by ItemPointers. Storages that
have different requirements will need careful additional thought across
the board.
iv) System catalogs cannot use pluggable storage. We continue to use
heap_open etc in the DDL code, in order not to make this more invasive
that it already is. We may lift this restriction later for specific
catalogs, as needed.
v) Currently, one Buffer may be associated with one HeapTuple living in a
slot; when the slot is cleared, the buffer pin is released. My current
patch moves the buffer pin to inside the heapam-based storage AM and the
buffer is released by the ->slot_clear_tuple method. The rationale for
doing this is that some storage AMs might want to keep several buffers
pinned at once, for example, and must not to release those pins
individually but in batches as the scan moves forwards (say a batch of
tuples in a columnar storage AM has column values spread across many
buffers; they must all be kept pinned until the scan has moved past the
whole set of tuples). But I'm not really sure that this is a great
design.
I welcome comments on these ideas. My patch for this is nowhere near
completion yet; expect things to change for items that I've overlooked,
but I hope I didn't overlook any major. If things are handwavy, it is
probably because I haven't fully figured them out yet.
--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
@ 2016-08-15 11:55 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
4 siblings, 0 replies; 138+ messages in thread
From: Alexander Korotkov @ 2016-08-15 11:55 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers
On Sat, Aug 13, 2016 at 2:15 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
> Many have expressed their interest in this topic, but I haven't seen any
> design of how it should work.
That's it. My design presented at PGCon was very sketchy, and I didn't
deliver any prototype yet.
> Here's my attempt; I've been playing with
> this for some time now and I think what I propose here is a good initial
> plan.
Great! It's nice to see you're working in this direction!
> This will allow us to write permanent table storage that works
> differently than heapam.c. At this stage, I haven't throught through
> whether this is going to allow extensions to define new storage modules;
> I am focusing on AMs that can coexist with heapam in core.
>
So, as I get you're proposing extendability to replace heap with something
another
but compatible with heap (with executor nodes, index access methods and so
on).
That's good, but what alternative storage access methods could be?
AFAICS, we can't fit here, for instance, another MVCC implementation (undo
log) or
in-memory storage or columnar storage. However, it seems that we would be
able to make compressed heap or alternative HOT/WARM tuples mechanism.
Correct me if I'm wrong.
ISTM you're proposing something quite orthogonal to my view
of pluggable storage engines. My idea, in general, was to extend FDW
mechanism
to let it be something manageable inside database (support for VACUUM,
defining
indexes and so on). But imperative was that it should have its own
executor nodes,
and it doesn't have to compatible with existing index access methods.
Therefore, I think my design presented at PGCon and your current proposal
are
about orthogonal features which could coexist.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
@ 2016-08-15 16:02 ` Robert Haas <robertmhaas@gmail.com>
2016-08-16 18:46 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
2016-08-18 14:44 ` Re: Pluggable storage Ants Aasma <ants.aasma@eesti.ee>
4 siblings, 2 replies; 138+ messages in thread
From: Robert Haas @ 2016-08-15 16:02 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers
On Fri, Aug 12, 2016 at 7:15 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
> Many have expressed their interest in this topic, but I haven't seen any
> design of how it should work. Here's my attempt; I've been playing with
> this for some time now and I think what I propose here is a good initial
> plan. This will allow us to write permanent table storage that works
> differently than heapam.c. At this stage, I haven't throught through
> whether this is going to allow extensions to define new storage modules;
> I am focusing on AMs that can coexist with heapam in core.
Thanks for taking a stab at this. I'd like to throw out a few concerns.
One, I'm worried that adding an additional layer of pointer-jumping is
going to slow things down and make Andres' work to speed up the
executor more difficult. I don't know that there is a problem there,
and if there is a problem I don't know what to do about it, but I
think it's something we need to consider. I am somewhat inclined to
believe that we need to restructure the executor in a bigger way so
that it passes around datums instead of tuples; I'm inclined to
believe that the current tuple-centric model is probably not optimal
even for the existing storage format. It seems even less likely to be
right for a data format in which fetching columns is more expensive
than currently, such as a columnar store.
Two, I think that we really need to think very hard about how the
query planner will interact with new heap storage formats. For
example, suppose cstore_fdw were rewritten as a new heap storage
format. Because ORC contains internal indexing structures with
characteristics somewhat similar to BRIN, many scans can be executed
much more efficiently than for our current heap storage format. If it
can be seen that an entire chunk will fail to match the quals, we can
skip the whole chunk. Some operations may permit additional
optimizations: for example, given SELECT count(*) FROM thing WHERE
quals, we may be able to push the COUNT(*) down into the heap access
layer. If it can be verified that EVERY tuple in a chunk will match
the quals, we can just increment the count by that number without
visiting each tuple individually. This could be really fast. These
kinds of query planner issues are generally why I have favored trying
to do something like this through the FDW interface, which already has
the right APIs for this kind of thing, even if we're not using them
all yet. I don't say that's the only way to crack this problem, but I
think we're going to find that a heap storage API that doesn't include
adequate query planner integration is not a very exciting thing.
Three, with respect to this limitation:
> iii) All tuples need to be identifiable by ItemPointers. Storages that
> have different requirements will need careful additional thought across
> the board.
I think it's a good idea for a first patch in this area to ignore (or
mostly ignore) this problem - e.g. maybe allow such storage formats
but refuse to create indexes on them. But eventually I think we're
going to want/need to do something about it. There are an awful lot
of interesting ideas that we can't pursue without addressing this.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2016-08-16 18:46 ` Andres Freund <andres@anarazel.de>
2016-08-18 07:58 ` Re: Pluggable storage Simon Riggs <simon@2ndquadrant.com>
1 sibling, 1 reply; 138+ messages in thread
From: Andres Freund @ 2016-08-16 18:46 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
> Thanks for taking a stab at this. I'd like to throw out a few concerns.
>
> One, I'm worried that adding an additional layer of pointer-jumping is
> going to slow things down and make Andres' work to speed up the
> executor more difficult. I don't know that there is a problem there,
> and if there is a problem I don't know what to do about it, but I
> think it's something we need to consider.
I'm quite concerned about that as well.
> I am somewhat inclined to
> believe that we need to restructure the executor in a bigger way so
> that it passes around datums instead of tuples; I'm inclined to
> believe that the current tuple-centric model is probably not optimal
> even for the existing storage format.
I actually prototyped that, and it's not an easy win so far. Column
extraction cost, even after significant optimization, is still often a
significant portion of the runtime. And e.g. projection only extracting
all columns, after evaluating a restrictive qual referring to an "early"
column, can be a significant win. We'd definitely have to give up on
extracting columns 0..n when accessing later columns... Hm.
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2016-08-16 18:46 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
@ 2016-08-18 07:58 ` Simon Riggs <simon@2ndquadrant.com>
2016-08-18 11:01 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2016-08-18 16:24 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
0 siblings, 2 replies; 138+ messages in thread
From: Simon Riggs @ 2016-08-18 07:58 UTC (permalink / raw)
To: Andres Freund <andres@anarazel.de>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 16 August 2016 at 19:46, Andres Freund <andres@anarazel.de> wrote:
> On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
>> Thanks for taking a stab at this. I'd like to throw out a few concerns.
>>
>> One, I'm worried that adding an additional layer of pointer-jumping is
>> going to slow things down and make Andres' work to speed up the
>> executor more difficult. I don't know that there is a problem there,
>> and if there is a problem I don't know what to do about it, but I
>> think it's something we need to consider.
>
> I'm quite concerned about that as well.
This objection would apply to all other proposals as well, FDW etc..
Do you see some way to add flexibility yet without adding a branch
point in the code?
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2016-08-16 18:46 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
2016-08-18 07:58 ` Re: Pluggable storage Simon Riggs <simon@2ndquadrant.com>
@ 2016-08-18 11:01 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 0 replies; 138+ messages in thread
From: Alexander Korotkov @ 2016-08-18 11:01 UTC (permalink / raw)
To: Simon Riggs <simon@2ndquadrant.com>; +Cc: Andres Freund <andres@anarazel.de>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Aug 18, 2016 at 10:58 AM, Simon Riggs <simon@2ndquadrant.com> wrote:
> On 16 August 2016 at 19:46, Andres Freund <andres@anarazel.de> wrote:
> > On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
> >> Thanks for taking a stab at this. I'd like to throw out a few concerns.
> >>
> >> One, I'm worried that adding an additional layer of pointer-jumping is
> >> going to slow things down and make Andres' work to speed up the
> >> executor more difficult. I don't know that there is a problem there,
> >> and if there is a problem I don't know what to do about it, but I
> >> think it's something we need to consider.
> >
> > I'm quite concerned about that as well.
>
> This objection would apply to all other proposals as well, FDW etc..
>
> Do you see some way to add flexibility yet without adding a branch
> point in the code?
>
It's impossible without branch point in code. The question is where this
branch should be located.
In particular, be can put this branch point into planner by defining
distinct executor nodes for each pluggable storage. In this case, each
storage would have own optimized executor nodes.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2016-08-16 18:46 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
2016-08-18 07:58 ` Re: Pluggable storage Simon Riggs <simon@2ndquadrant.com>
@ 2016-08-18 16:24 ` Andres Freund <andres@anarazel.de>
1 sibling, 0 replies; 138+ messages in thread
From: Andres Freund @ 2016-08-18 16:24 UTC (permalink / raw)
To: Simon Riggs <simon@2ndquadrant.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 2016-08-18 08:58:11 +0100, Simon Riggs wrote:
> On 16 August 2016 at 19:46, Andres Freund <andres@anarazel.de> wrote:
> > On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
> >> Thanks for taking a stab at this. I'd like to throw out a few concerns.
> >>
> >> One, I'm worried that adding an additional layer of pointer-jumping is
> >> going to slow things down and make Andres' work to speed up the
> >> executor more difficult. I don't know that there is a problem there,
> >> and if there is a problem I don't know what to do about it, but I
> >> think it's something we need to consider.
> >
> > I'm quite concerned about that as well.
>
> This objection would apply to all other proposals as well, FDW etc..
Not really. The place you draw your boundary significantly influences
where and how much of a price you pay. Having another indirection
inside HeapTuple - which is accessed in many many places, is something
different from having a seqscan equivalent, which returns you a batch of
already deformed tuples in array form. In the latter case there's one
additional indirection per batch of tuples, in the former there's many
for each tuple.
> Do you see some way to add flexibility yet without adding a branch
> point in the code?
I'm not even saying that the approach of doing the indirection inside
the HeapTuple replacement is a no-go, just that it concerns me. I do
think that working on only lugging arround values/isnull arrays is
something that I could see working better, if some problems are
addressed beforehand.
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2016-08-18 14:44 ` Ants Aasma <ants.aasma@eesti.ee>
2016-08-18 15:14 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
1 sibling, 1 reply; 138+ messages in thread
From: Ants Aasma @ 2016-08-18 14:44 UTC (permalink / raw)
To: Andres Freund <andres@anarazel.de>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Aug 16, 2016 at 9:46 PM, Andres Freund <andres@anarazel.de> wrote:
> On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
>> I am somewhat inclined to
>> believe that we need to restructure the executor in a bigger way so
>> that it passes around datums instead of tuples; I'm inclined to
>> believe that the current tuple-centric model is probably not optimal
>> even for the existing storage format.
>
> I actually prototyped that, and it's not an easy win so far. Column
> extraction cost, even after significant optimization, is still often a
> significant portion of the runtime. And e.g. projection only extracting
> all columns, after evaluating a restrictive qual referring to an "early"
> column, can be a significant win. We'd definitely have to give up on
> extracting columns 0..n when accessing later columns... Hm.
What about going even further than [1] in converting the executor to
being opcode based and merging projection and qual evaluation to a
single pass? Optimizer would then have some leeway about how to order
column extraction and qual evaluation. Might even be worth it to
special case some functions as separate opcodes (e.g. int4eq,
timestamp_lt).
Regards,
Ants Aasma
[1] https://www.postgresql.org/message-id/20160714011850.bd5zhu35szle3n3c@alap3.anarazel.de
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 16:02 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2016-08-18 14:44 ` Re: Pluggable storage Ants Aasma <ants.aasma@eesti.ee>
@ 2016-08-18 15:14 ` Andres Freund <andres@anarazel.de>
0 siblings, 0 replies; 138+ messages in thread
From: Andres Freund @ 2016-08-18 15:14 UTC (permalink / raw)
To: Ants Aasma <ants.aasma@eesti.ee>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On August 18, 2016 7:44:50 AM PDT, Ants Aasma <ants.aasma@eesti.ee> wrote:
>On Tue, Aug 16, 2016 at 9:46 PM, Andres Freund <andres@anarazel.de>
>wrote:
>> On 2016-08-15 12:02:18 -0400, Robert Haas wrote:
>>> I am somewhat inclined to
>>> believe that we need to restructure the executor in a bigger way so
>>> that it passes around datums instead of tuples; I'm inclined to
>>> believe that the current tuple-centric model is probably not optimal
>>> even for the existing storage format.
>>
>> I actually prototyped that, and it's not an easy win so far. Column
>> extraction cost, even after significant optimization, is still often
>a
>> significant portion of the runtime. And e.g. projection only
>extracting
>> all columns, after evaluating a restrictive qual referring to an
>"early"
>> column, can be a significant win. We'd definitely have to give up on
>> extracting columns 0..n when accessing later columns... Hm.
>
>What about going even further than [1] in converting the executor to
>being opcode based and merging projection and qual evaluation to a
>single pass? Optimizer would then have some leeway about how to order
>column extraction and qual evaluation. Might even be worth it to
>special case some functions as separate opcodes (e.g. int4eq,
>timestamp_lt).
>
>Regards,
>Ants Aasma
>
>[1]
>https://www.postgresql.org/message-id/20160714011850.bd5zhu35szle3n3c@alap3.anarazel.de
Good question. I think I have a reasonable answer, but lets discuss that in the other thread.
Andres
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
@ 2016-08-15 18:09 ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
4 siblings, 0 replies; 138+ messages in thread
From: Anastasia Lubennikova @ 2016-08-15 18:09 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndQuadrant.com>; pgsql-hackers
13.08.2016 02:15, Alvaro Herrera:
> Many have expressed their interest in this topic, but I haven't seen any
> design of how it should work. Here's my attempt; I've been playing with
> this for some time now and I think what I propose here is a good initial
> plan. This will allow us to write permanent table storage that works
> differently than heapam.c. At this stage, I haven't throught through
> whether this is going to allow extensions to define new storage modules;
> I am focusing on AMs that can coexist with heapam in core.
Hello,
thank you for starting this topic.
I am working on a very similar proposal in another thread.
https://commitfest.postgresql.org/10/700/
At first glance our drafts are very similar. I hope it means that we are
moving in the right direction. It's great that your proposal is focused on
interactions with executor while mine are mostly about internals of new
StorageAm interface and interactions with a buffer and storage management.
Please read and comment https://wiki.postgresql.org/wiki/HeapamRefactoring
I'll leave more concrete comments tomorrow.
Thank you for the explicit description of issues.
> The design starts with a new row type in pg_am, of type "s" (for "storage").
> The handler function returns a struct of node StorageAmRoutine. This
> contains functions for 1) scans (beginscan, getnext, endscan) 2) tuples
> (tuple_insert/update/delete/lock, as well as set_oid, get_xmin and the
> like), and operations on tuples that are part of slots (tuple_deform,
> materialize).
> To support this, we introduce StorageTuple and StorageScanDesc.
> StorageTuples represent a physical tuple coming from some storage AM.
> It is necessary to have a pointer to a StorageAmRoutine in order to
> manipulate the tuple. For heapam.c, a StorageTuple is just a HeapTuple.
>
> RelationData gains ->rd_stamroutine which is a pointer to the
> StorageAmRoutine for the relation in question. Similarly,
> TupleTableSlot is augmented with a link to the StorageAmRoutine to
> handle the StorageTuple it contains (probably in most cases it's set at
> the same time as the tupdesc). This implies that routines such as
> ExecAssignScanType need to pass down the StorageAmRoutine from the
> relation to the slot.
>
> The executor is modified so that instead of calling heap_insert etc
> directly, it uses rel->rd_stamroutine to call these methods. The
> executor is still in charge of dealing with indexes, constraints, and
> any other thing that's not the tuple storage itself (this is one major
> point in which this differs from FDWs). This all looks simple enough,
> with one exception and a few notes:
>
> exception a) ExecMaterializeSlot needs special consideration. This is
> used in two different ways: a1) is the stated "make tuple independent
> from any underlying storage" point, which is handled by
> ExecMaterializeSlot itself and calling a method from the storage AM to
> do any byte copying as needed. ExecMaterializeSlot no longer returns a
> HeapTuple, because there might not be any. The second usage pattern a2)
> is to create a HeapTuple that's passed to other modules which only deal
> with HT and not slots (triggers are the main case I noticed, but I think
> there are others such as the executor itself wanting tuples as Datum for
> some reason). For the moment I'm handling this by having a new
> ExecHeapifyTuple which creates a HeapTuple from a slot, regardless of
> the original tuple format.
>
> note b) EvalPlanQual currently maintains an array of HeapTuple in
> EState->es_epqTuple. I think it works to replace that with an array of
> StorageTuples; EvalPlanQualFetch needs to call the StorageAmRoutine
> methods in order to interact with it. Other than those changes, it
> seems okay.
>
> note c) nodeSubplan has curTuple as a HeapTuple. It seems simple
> to replace this with an independent slot-based tuple.
>
> note d) grp_firstTuple in nodeAgg / nodeSetOp. These are less
> simple than the above, but replacing the HeapTuple with a slot-based
> tuple seems doable too.
>
> note e) nodeLockRows uses lr_curtuples to feed EvalPlanQual.
> TupleTableSlot also seems a good replacement. This has fallout in other
> users of EvalPlanQual, too.
>
> note f) More widespread, MinimalTuples currently use a tweaked HeapTuple
> format. In the long run, it may be possible to replace them with a
> separate storage module that's specifically designed to handle tuples
> meant for tuplestores etc. That may simplify TupleTableSlot and
> execTuples. For the moment we keep the tts_mintuple as it is. Whenever
> a tuple is not already in heap format, we heapify it in order to put in
> the store.
>
>
> The current heapam.c routines need some changes. Currently, practice is
> that heap_insert, heap_multi_insert, heap_fetch, heap_update scribble on
> their input tuples to set the resulting ItemPointer in tuple->t_self.
> This is messy if we want StorageTuples to be abstract. I'm changing
> this so that the resulting ItemPointer is returned in a separate output
> argument; the tuple itself is left alone. This is somewhat messy in the
> case of heap_multi_insert because it returns several items; I think it's
> acceptable to return an array of ItemPointers in the same order as the
> input tuples. This works fine for the only caller, which is COPY in
> batch mode. For the other routines, they don't really care where the
> TID is returned AFAICS.
>
>
> Additional noteworthy items:
>
> i) Speculative insertion: the speculative insertion token is no longer
> installed directly in the heap tuple by the executor (of course).
> Instead, the token becomes part of the slot. When the tuple_insert
> method is called, the insertion routine is in charge of setting the
> token from the slot into the storage tuple. Executor is in charge of
> calling method->speculative_finish() / abort() once the insertion has
> been confirmed by the indexes.
>
> ii) execTuples has additional accessors for tuples-in-slot, such as
> ExecFetchSlotTuple and friends. I expect to have some of them to return
> abstract StorageTuples, others HeapTuple or MinimalTuples (possibly
> wrapped in Datum), depending on callers. We might be able to cut down
> on these later; my first cut will try to avoid API changes to keep
> fallout to a minimum.
>
> iii) All tuples need to be identifiable by ItemPointers. Storages that
> have different requirements will need careful additional thought across
> the board.
>
> iv) System catalogs cannot use pluggable storage. We continue to use
> heap_open etc in the DDL code, in order not to make this more invasive
> that it already is. We may lift this restriction later for specific
> catalogs, as needed.
>
> v) Currently, one Buffer may be associated with one HeapTuple living in a
> slot; when the slot is cleared, the buffer pin is released. My current
> patch moves the buffer pin to inside the heapam-based storage AM and the
> buffer is released by the ->slot_clear_tuple method. The rationale for
> doing this is that some storage AMs might want to keep several buffers
> pinned at once, for example, and must not to release those pins
> individually but in batches as the scan moves forwards (say a batch of
> tuples in a columnar storage AM has column values spread across many
> buffers; they must all be kept pinned until the scan has moved past the
> whole set of tuples). But I'm not really sure that this is a great
> design.
>
>
> I welcome comments on these ideas. My patch for this is nowhere near
> completion yet; expect things to change for items that I've overlooked,
> but I hope I didn't overlook any major. If things are handwavy, it is
> probably because I haven't fully figured them out yet.
>
--
Anastasia Lubennikova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
@ 2016-08-17 13:01 ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-17 17:03 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
4 siblings, 1 reply; 138+ messages in thread
From: Anastasia Lubennikova @ 2016-08-17 13:01 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndQuadrant.com>; pgsql-hackers
13.08.2016 02:15, Alvaro Herrera:
> Many have expressed their interest in this topic, but I haven't seen any
> design of how it should work. Here's my attempt; I've been playing with
> this for some time now and I think what I propose here is a good initial
> plan. This will allow us to write permanent table storage that works
> differently than heapam.c. At this stage, I haven't throught through
> whether this is going to allow extensions to define new storage modules;
> I am focusing on AMs that can coexist with heapam in core.
>
> The design starts with a new row type in pg_am, of type "s" (for "storage").
> The handler function returns a struct of node StorageAmRoutine. This
> contains functions for 1) scans (beginscan, getnext, endscan) 2) tuples
> (tuple_insert/update/delete/lock, as well as set_oid, get_xmin and the
> like), and operations on tuples that are part of slots (tuple_deform,
> materialize).
>
> To support this, we introduce StorageTuple and StorageScanDesc.
> StorageTuples represent a physical tuple coming from some storage AM.
> It is necessary to have a pointer to a StorageAmRoutine in order to
> manipulate the tuple. For heapam.c, a StorageTuple is just a HeapTuple.
StorageTuples concept looks really cool. I've got some questions on
details of implementation.
Do StorageTuples have fields common to all implementations?
Or StorageTuple is totally abstract structure that has nothing to do
with data, except pointing to it?
I mean, now we already have HeapTupleData structure, which is a pretty
good candidate to replace with StorageTuple.
It's already widely used in executor and moreover, it's the only structure
(except MinimalTuples and all those crazy optimizations) that works with
tuples, both extracted from the page or created on-the-fly in executor node.
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
We can simply change t_data type from HeapTupleHeader to Pointer.
And maybe add a "t_handler" field that points out to handler functions.
I don't sure if it will be a name of StorageAm, or its OID, or maybe the
main function itself. Although, If I'm not mistaken, we always have
RelationData when we want to operate the tuple, so having t_handler
in the StorageTuple is excessive.
typedef struct StorageTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
Pointer t_data; /* -> tuple header and data
* This field should never be
accessed directly,
* only via StorageAm handler
functions,
* because we don't know
underlying data structure.
*/
??? t_handler; /* StorageAm that knows what to do
with the tuple */
} StorageTupleData
;
This approach allows to minimize code changes and ensure that we
won't miss any function that handles tuples.
Do you see any weak points of the suggestion?
What design do you use in your prototype?
> RelationData gains ->rd_stamroutine which is a pointer to the
> StorageAmRoutine for the relation in question. Similarly,
> TupleTableSlot is augmented with a link to the StorageAmRoutine to
> handle the StorageTuple it contains (probably in most cases it's set at
> the same time as the tupdesc). This implies that routines such as
> ExecAssignScanType need to pass down the StorageAmRoutine from the
> relation to the slot.
If we already have this pointer in t_handler as described below,
we don't need to pass it between functions and slots.
> The executor is modified so that instead of calling heap_insert etc
> directly, it uses rel->rd_stamroutine to call these methods. The
> executor is still in charge of dealing with indexes, constraints, and
> any other thing that's not the tuple storage itself (this is one major
> point in which this differs from FDWs). This all looks simple enough,
> with one exception and a few notes:
That is exactly what I tried to describe in my proposal.
Chapter "Relation management". I'm sure, you've already noticed
that it will require huge source code cleaning. I've carefully read
the sources and found "violators" of abstraction in src/backend/commands.
The list is attached to the wiki page
https://wiki.postgresql.org/wiki/HeapamRefactoring.
Except these, there are some pretty strange and unrelated functions in
src/backend/catalog.
I'm willing to fix them, but I'd like to synchronize our efforts.
> exception a) ExecMaterializeSlot needs special consideration. This is
> used in two different ways: a1) is the stated "make tuple independent
> from any underlying storage" point, which is handled by
> ExecMaterializeSlot itself and calling a method from the storage AM to
> do any byte copying as needed. ExecMaterializeSlot no longer returns a
> HeapTuple, because there might not be any. The second usage pattern a2)
> is to create a HeapTuple that's passed to other modules which only deal
> with HT and not slots (triggers are the main case I noticed, but I think
> there are others such as the executor itself wanting tuples as Datum for
> some reason). For the moment I'm handling this by having a new
> ExecHeapifyTuple which creates a HeapTuple from a slot, regardless of
> the original tuple format.
Yes, triggers are a very special case. Thank you for the explanation.
That still goes well with my suggestion of a format.
Nothing to do, just substitute t_data with proper HeapTupleHeader
representation. I think it's a job for StorageAm. Let's say each StorageAm
must have stam_to_heaptuple() function and opposite function
stam_from_heaptuple().
> note b) EvalPlanQual currently maintains an array of HeapTuple in
> EState->es_epqTuple. I think it works to replace that with an array of
> StorageTuples; EvalPlanQualFetch needs to call the StorageAmRoutine
> methods in order to interact with it. Other than those changes, it
> seems okay.
>
> note c) nodeSubplan has curTuple as a HeapTuple. It seems simple
> to replace this with an independent slot-based tuple.
>
> note d) grp_firstTuple in nodeAgg / nodeSetOp. These are less
> simple than the above, but replacing the HeapTuple with a slot-based
> tuple seems doable too.
>
> note e) nodeLockRows uses lr_curtuples to feed EvalPlanQual.
> TupleTableSlot also seems a good replacement. This has fallout in other
> users of EvalPlanQual, too.
>
> note f) More widespread, MinimalTuples currently use a tweaked HeapTuple
> format. In the long run, it may be possible to replace them with a
> separate storage module that's specifically designed to handle tuples
> meant for tuplestores etc. That may simplify TupleTableSlot and
> execTuples. For the moment we keep the tts_mintuple as it is. Whenever
> a tuple is not already in heap format, we heapify it in order to put in
> the store.
I wonder, do we really need MinimalTuples to support all formats?
> The current heapam.c routines need some changes. Currently, practice is
> that heap_insert, heap_multi_insert, heap_fetch, heap_update scribble on
> their input tuples to set the resulting ItemPointer in tuple->t_self.
> This is messy if we want StorageTuples to be abstract. I'm changing
> this so that the resulting ItemPointer is returned in a separate output
> argument; the tuple itself is left alone. This is somewhat messy in the
> case of heap_multi_insert because it returns several items; I think it's
> acceptable to return an array of ItemPointers in the same order as the
> input tuples. This works fine for the only caller, which is COPY in
> batch mode. For the other routines, they don't really care where the
> TID is returned AFAICS.
>
>
> Additional noteworthy items:
>
> i) Speculative insertion: the speculative insertion token is no longer
> installed directly in the heap tuple by the executor (of course).
> Instead, the token becomes part of the slot. When the tuple_insert
> method is called, the insertion routine is in charge of setting the
> token from the slot into the storage tuple. Executor is in charge of
> calling method->speculative_finish() / abort() once the insertion has
> been confirmed by the indexes.
>
> ii) execTuples has additional accessors for tuples-in-slot, such as
> ExecFetchSlotTuple and friends. I expect to have some of them to return
> abstract StorageTuples, others HeapTuple or MinimalTuples (possibly
> wrapped in Datum), depending on callers. We might be able to cut down
> on these later; my first cut will try to avoid API changes to keep
> fallout to a minimum.
I'd suggest replacing all occurrences of HeapTuple with StorageTuple.
Do you see any problems with it?
> iii) All tuples need to be identifiable by ItemPointers. Storages that
> have different requirements will need careful additional thought across
> the board.
For a start, we can simply deny secondary indexes for these storages
or require a function that converts tuple identifier inside the storage to
ItemPointer suitable for an index.
> iv) System catalogs cannot use pluggable storage. We continue to use
> heap_open etc in the DDL code, in order not to make this more invasive
> that it already is. We may lift this restriction later for specific
> catalogs, as needed.
+1
>
> v) Currently, one Buffer may be associated with one HeapTuple living in a
> slot; when the slot is cleared, the buffer pin is released. My current
> patch moves the buffer pin to inside the heapam-based storage AM and the
> buffer is released by the ->slot_clear_tuple method. The rationale for
> doing this is that some storage AMs might want to keep several buffers
> pinned at once, for example, and must not to release those pins
> individually but in batches as the scan moves forwards (say a batch of
> tuples in a columnar storage AM has column values spread across many
> buffers; they must all be kept pinned until the scan has moved past the
> whole set of tuples). But I'm not really sure that this is a great
> design.
Frankly, I doubt that it's real to implement columnar storage just as
a variant of pluggable storage. It requires a lot of changes in executor
and optimizer and so on, which are hardly compatible with existing
tuple-oriented model. However I'm not so good in this area, so if you
feel that it's possible, go ahead.
> I welcome comments on these ideas. My patch for this is nowhere near
> completion yet; expect things to change for items that I've overlooked,
> but I hope I didn't overlook any major. If things are handwavy, it is
> probably because I haven't fully figured them out yet.
Thank you again for beginning the big project.
Looking forward to the prototype. I think it will make the discussion
more concrete and useful.
--
Anastasia Lubennikova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-17 13:01 ` Re: Pluggable storage Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
@ 2016-08-17 17:03 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2016-08-18 11:23 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alvaro Herrera @ 2016-08-17 17:03 UTC (permalink / raw)
To: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; +Cc: pgsql-hackers
Anastasia Lubennikova wrote:
> 13.08.2016 02:15, Alvaro Herrera:
> >To support this, we introduce StorageTuple and StorageScanDesc.
> >StorageTuples represent a physical tuple coming from some storage AM.
> >It is necessary to have a pointer to a StorageAmRoutine in order to
> >manipulate the tuple. For heapam.c, a StorageTuple is just a HeapTuple.
>
> StorageTuples concept looks really cool. I've got some questions on
> details of implementation.
>
> Do StorageTuples have fields common to all implementations?
> Or StorageTuple is totally abstract structure that has nothing to do
> with data, except pointing to it?
>
> I mean, now we already have HeapTupleData structure, which is a pretty
> good candidate to replace with StorageTuple.
I was planning to replace all uses of HeapTuple in the executor with
StorageTuple, actually. But the main reason I would like to avoid
HeapTupleData itself is that it contains an assumption that there is a
single palloc chunk that contains the tuple (t_len and t_data). This
might not be true in representations that split the tuple, for example
in columnar storage where you have one column in page A and another
column in page B, for the same tuple. I suppose there might be some
point to keeping t_tableOid and t_self, though.
> And maybe add a "t_handler" field that points out to handler functions.
> I don't sure if it will be a name of StorageAm, or its OID, or maybe the
> main function itself. Although, If I'm not mistaken, we always have
> RelationData when we want to operate the tuple, so having t_handler
> in the StorageTuple is excessive.
Yeah, I think the RelationData (or more precisely the StorageAmRoutine)
is going to be available always, so I don't think we need a pointer in
the tuple itself.
> This approach allows to minimize code changes and ensure that we
> won't miss any function that handles tuples.
>
> Do you see any weak points of the suggestion?
> What design do you use in your prototype?
It's currently a "void *" pointer in my prototype.
> >RelationData gains ->rd_stamroutine which is a pointer to the
> >StorageAmRoutine for the relation in question. Similarly,
> >TupleTableSlot is augmented with a link to the StorageAmRoutine to
> >handle the StorageTuple it contains (probably in most cases it's set at
> >the same time as the tupdesc). This implies that routines such as
> >ExecAssignScanType need to pass down the StorageAmRoutine from the
> >relation to the slot.
>
> If we already have this pointer in t_handler as described below,
> we don't need to pass it between functions and slots.
I think it's better to have it in slots, so you can install multiple
tuples in the slot without having to change the routine pointers each
time.
> >The executor is modified so that instead of calling heap_insert etc
> >directly, it uses rel->rd_stamroutine to call these methods. The
> >executor is still in charge of dealing with indexes, constraints, and
> >any other thing that's not the tuple storage itself (this is one major
> >point in which this differs from FDWs). This all looks simple enough,
> >with one exception and a few notes:
>
> That is exactly what I tried to describe in my proposal.
> Chapter "Relation management". I'm sure, you've already noticed
> that it will require huge source code cleaning. I've carefully read
> the sources and found "violators" of abstraction in src/backend/commands.
> The list is attached to the wiki page
> https://wiki.postgresql.org/wiki/HeapamRefactoring.
>
> Except these, there are some pretty strange and unrelated functions in
> src/backend/catalog.
> I'm willing to fix them, but I'd like to synchronize our efforts.
I very much would like to stay away from touching src/backend/catalog,
which are the functions that deal with system catalogs. We can simply
say that system catalogs are hardcoded to use heapam.c storage for now.
If we later see a need to enable some particular catalog using a
different storage implementation, we can change the code for that
specific catalog in src/backend/catalog and everywhere else, to use the
abstract API instead of hardcoding heap_insert etc. But that can be
left for a second pass. (This is my point "iv" further below, to which
you said "+1").
> Nothing to do, just substitute t_data with proper HeapTupleHeader
> representation. I think it's a job for StorageAm. Let's say each StorageAm
> must have stam_to_heaptuple() function and opposite function
> stam_from_heaptuple().
Hmm, yeah, that also works. We'd have to check again whether it's more
convenient to start as a slot rather than a StorageTuple. AFAICS the
trigger.c code is all starting from a slot, so it makes sense to have
the conversion use the slot code -- that way, there's no need for each
storageAM to re-implement conversion to HeapTuple.
> >note f) More widespread, MinimalTuples currently use a tweaked HeapTuple
> >format. In the long run, it may be possible to replace them with a
> >separate storage module that's specifically designed to handle tuples
> >meant for tuplestores etc. That may simplify TupleTableSlot and
> >execTuples. For the moment we keep the tts_mintuple as it is. Whenever
> >a tuple is not already in heap format, we heapify it in order to put in
> >the store.
> I wonder, do we really need MinimalTuples to support all formats?
Sure. I wouldn't want to say "you can create table in columnar storage
format, but if you do, these tables cannot use hash join".
> >ii) execTuples has additional accessors for tuples-in-slot, such as
> >ExecFetchSlotTuple and friends. I expect to have some of them to return
> >abstract StorageTuples, others HeapTuple or MinimalTuples (possibly
> >wrapped in Datum), depending on callers. We might be able to cut down
> >on these later; my first cut will try to avoid API changes to keep
> >fallout to a minimum.
>
> I'd suggest replacing all occurrences of HeapTuple with StorageTuple.
> Do you see any problems with it?
The HeapTuple-in-datum representation, as I recall, is used in the SQL
function manager; maybe other places too. Maybe there's a way to fix
that layer so that it uses StorageTuple instead, but I prefer not to
touch it in the first phase. We can fix it later. This is already a
big enough patch ...
> >iii) All tuples need to be identifiable by ItemPointers. Storages that
> >have different requirements will need careful additional thought across
> >the board.
>
> For a start, we can simply deny secondary indexes for these storages
> or require a function that converts tuple identifier inside the storage to
> ItemPointer suitable for an index.
Umm. I don't think rejecting secondary indexes would work very well. I
think we can lift this limitation later; we just need to change the
IndexTuple abstraction so that it doesn't rely on ItemPointer as
currently.
> >v) Currently, one Buffer may be associated with one HeapTuple living in a
> >slot; when the slot is cleared, the buffer pin is released. My current
> >patch moves the buffer pin to inside the heapam-based storage AM and the
> >buffer is released by the ->slot_clear_tuple method. The rationale for
> >doing this is that some storage AMs might want to keep several buffers
> >pinned at once, for example, and must not to release those pins
> >individually but in batches as the scan moves forwards (say a batch of
> >tuples in a columnar storage AM has column values spread across many
> >buffers; they must all be kept pinned until the scan has moved past the
> >whole set of tuples). But I'm not really sure that this is a great
> >design.
>
> Frankly, I doubt that it's real to implement columnar storage just as
> a variant of pluggable storage. It requires a lot of changes in executor
> and optimizer and so on, which are hardly compatible with existing
> tuple-oriented model. However I'm not so good in this area, so if you
> feel that it's possible, go ahead.
Well, not *just* as a variant of pluggable storage. This thread is just
one sub-project inside the greater project to enable column-oriented
storage; that includes further changes to executor, too, but I haven't
discussed those in this proposal. I mentioned all this in Brussels'
developer meeting earlier this year. (There I mostly talked about
vertical partitioning, which is a different subproject that I've put
aside for the moment, but really it's all part of the same thing.)
https://wiki.postgresql.org/wiki/Future_of_storage
Thanks for reading!
--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-17 13:01 ` Re: Pluggable storage Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-17 17:03 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2016-08-18 11:23 ` Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 0 replies; 138+ messages in thread
From: Amit Kapila @ 2016-08-18 11:23 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Anastasia Lubennikova <a.lubennikova@postgrespro.ru>; pgsql-hackers
On Wed, Aug 17, 2016 at 10:33 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
> Anastasia Lubennikova wrote:
>>
>> Except these, there are some pretty strange and unrelated functions in
>> src/backend/catalog.
>> I'm willing to fix them, but I'd like to synchronize our efforts.
>
> I very much would like to stay away from touching src/backend/catalog,
> which are the functions that deal with system catalogs. We can simply
> say that system catalogs are hardcoded to use heapam.c storage for now.
>
Does this mean that if any storage needs to access system catalog
information, they need to be aware of HeapTuple and other required
stuff like syscache? Again, if they need to update some stats or
something like that, they need to be aware of heap tuple format.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
@ 2016-08-24 18:22 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
4 siblings, 1 reply; 138+ messages in thread
From: Alvaro Herrera @ 2016-08-24 18:22 UTC (permalink / raw)
To: pgsql-hackers
Alvaro Herrera wrote:
> Many have expressed their interest in this topic, but I haven't seen any
> design of how it should work. Here's my attempt; I've been playing with
> this for some time now and I think what I propose here is a good initial
> plan.
I regret to announce that I'll have to stay away from this topic for a
little while, as I have another project taking priority. I expect to
return to this shortly thereafter, hopefully in time to get it done for
pg10.
If anyone is interested in helping with the (currently not compilable)
patch I have, please mail me privately and we can discuss.
--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2016-10-13 20:26 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alvaro Herrera @ 2016-10-13 20:26 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>
I have sent the partial patch I have to Hari Babu Kommi. We expect that
he will be able to further this goal some more.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-06-13 01:50 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 12:16 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-08-08 04:21 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-15 06:53 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
0 siblings, 4 replies; 138+ messages in thread
From: Haribabu Kommi @ 2017-06-13 01:50 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers
On Fri, Oct 14, 2016 at 7:26 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
> I have sent the partial patch I have to Hari Babu Kommi. We expect that
> he will be able to further this goal some more.
Thanks Alvaro for sharing your development patch.
Most of the patch design is same as described by Alvaro in the first mail
[1].
I will detail the modifications, pending items and open items (needs
discussion)
to implement proper pluggable storage.
Here I attached WIP patches to support pluggable storage. The patch series
are may not work individually. Still so many things are under development.
These patches are just to share the approach of the current development.
Some notable changes that I did to make the patch work:
1. Added storageam handler to the slot, this is because not all places
the relation is not available in handy.
2. Retained the minimal Tuple in the slot, as this is used in HASH join.
As per the first version, I feel it is fine to allow creating HeapTuple
format data.
Thanks everyone for sharing their ideas in the developer's unconference at
PGCon Ottawa.
Pending items:
1. Replacement of Tuple with slot in Trigger functionality
2. Replacement of Tuple with Slot from storage handler functions.
3. Remove/minimize the use of HeapTuple as a Datum.
4. Replace all references of HeapScanDesc with StorageScanDesc
5. Planner changes to consider the relation storage during the planning.
6. Any planner changes based on the discussion of open items?
7. some Executor changes to consider the storage advantages?
Open Items:
1. The BitmapHeapScan and TableSampleScan are tightly coupled with
HeapTuple and HeapScanDesc, So these scans are directly operating
on those structures and providing the result.
These scan types may not be applicable to different storage formats.
So how to handle them?
Currently my goal to provide a basic infrastructure of pluggable storage as
a first step and later improve it further to improve the performance by
taking the advantage of storage.
[1] - https://www.postgresql.org/message-id/20160812231527.
GA690404%40alvherre.pgsql
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0010-compilation-fixes.patch (1.2K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/3-0010-compilation-fixes.patch)
download | inline diff:
From 948bcd88098b4abf8356bd1dc3240d1cb80d847e Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 14:44:19 +1000
Subject: [PATCH 10/10] compilation fixes
---
src/backend/access/heap/tuptoaster.c | 1 +
src/backend/executor/nodeModifyTable.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index aa5a45d..99e8018 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageamapi.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 58827e0..0f985c2 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -40,6 +40,7 @@
#include "access/htup_details.h"
#include "access/storageamapi.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
--
2.7.4.windows.1
[application/octet-stream] 0001-Create-Access-method-change-to-include-storage-handl.patch (7.0K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/4-0001-Create-Access-method-change-to-include-storage-handl.patch)
download | inline diff:
From 318d7dea1f487aff1f8f8bbde101c7c6b6d84fb3 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Thu, 1 Jun 2017 15:09:45 +1000
Subject: [PATCH 01/10] Create Access method change to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 5 +++++
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
6 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa..33079c1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2822331..7b5ed84 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5153,16 +5155,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be79353..0a7e0a3 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 9b6dc38..1857b95 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
@@ -84,4 +85,8 @@ DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 772 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 772
+
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 460cdb9..e232006 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3826,6 +3826,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 336 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 337 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 345e916..3170bc7 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
--
2.7.4.windows.1
[application/octet-stream] 0002-Regression-suite-update-according-to-the-new-access-.patch (2.9K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/5-0002-Regression-suite-update-according-to-the-new-access-.patch)
download | inline diff:
From 6f75ae6ebddc30d5c725176f97b1f300b52616af Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Thu, 1 Jun 2017 15:17:26 +1000
Subject: [PATCH 02/10] Regression suite update according to the new access
method
---
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
2 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1d7629f..c69fa4c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1701,11 +1701,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index bf2edb5..c0cdf61 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1146,15 +1146,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.7.4.windows.1
[application/octet-stream] 0003-Storage-access-method-API-functions.patch (13.3K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/6-0003-Storage-access-method-API-functions.patch)
download | inline diff:
From 8094682bf16e25873376b29ffd0f8edae9a6d735 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Thu, 1 Jun 2017 15:22:04 +1000
Subject: [PATCH 03/10] Storage access method API functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/storageamapi.c | 103 +++++++++++++++
src/include/access/storageamapi.h | 228 +++++++++++++++++++++++++++++++++
3 files changed, 333 insertions(+), 1 deletion(-)
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496..35e7b92 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000..def2029
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine *HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000..87130c9
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,228 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef HeapScanDesc (*scan_begin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys,
+ ScanKey key);
+typedef HeapScanDesc (*scan_begin_catalog_function) (Relation relation,
+ int nkeys,
+ ScanKey key);
+typedef HeapScanDesc (*scan_begin_strat_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys,
+ ScanKey key,
+ bool allow_strat,
+ bool allow_sync);
+typedef HeapScanDesc (*scan_begin_bm_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys,
+ ScanKey key);
+typedef HeapScanDesc (*scan_begin_sampling_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys,
+ ScanKey key,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode);
+typedef void (*scan_setlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple (*scan_getnext_function) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef void (*scan_end_function) (HeapScanDesc scan);
+
+
+typedef void (*scan_getpage_function) (HeapScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_function) (HeapScanDesc scan, ScanKey key);
+typedef void (*scan_rescan_set_params_function) (HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*scan_update_snapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef Oid (*tuple_insert_function) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate,
+ ItemPointer iptr);
+
+typedef HTSU_Result (*tuple_delete_function) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*tuple_update_function) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode,
+ ItemPointer iptr);
+typedef bool (*tuple_fetch_function) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*tuple_lock_function) (Relation relation,
+ ItemPointer tid,
+ StorageTuple tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef TransactionId (*tuple_get_xmin_function) (StorageTuple tuple);
+
+typedef TransactionId (*tuple_get_updated_xid_function) (StorageTuple tuple);
+
+typedef CommandId (*tuple_get_cmin_function) (StorageTuple tuple);
+
+typedef ItemPointer (*tuple_get_itempointer_function) (StorageTuple tuple);
+
+typedef ItemPointerData (*tuple_get_ctid_function) (StorageTuple tuple);
+
+typedef void (*slot_set_tuple_oid_function) (TupleTableSlot *slot, Oid tupleoid);
+
+typedef StorageTuple (*tuple_from_datum_function) (Datum data, Oid tableoid);
+
+typedef bool (* tuple_is_heaponly_function) (StorageTuple tuple);
+
+typedef void (*slot_store_tuple_function) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree);
+typedef void (*slot_clear_tuple_function) (TupleTableSlot *slot);
+typedef void (*slot_materialize_tuple_function) (TupleTableSlot *slot);
+typedef Datum (*slot_getattr_function) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*slot_getallattrs_function) (TupleTableSlot *slot);
+typedef void (*slot_getsomeattrs_function) (TupleTableSlot *slot, int attnum);
+typedef bool (*slot_attisnull_function) (TupleTableSlot *slot, int attnum);
+
+typedef HeapTuple (*slot_copy_tuple_function) (TupleTableSlot *slot);
+typedef MinimalTuple (*slot_copy_min_tuple_function) (TupleTableSlot *slot);
+typedef HeapTuple (*slot_get_tuple_function) (TupleTableSlot *slot);
+typedef bool (*slot_is_physical_tuple_function) (TupleTableSlot *slot);
+
+typedef void (*slot_update_tableoid_function) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*tuple_set_speculative_token_function) (TupleTableSlot *slot,
+ uint32 specToken);
+typedef void (*speculative_finish_function) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*speculative_abort_function) (Relation rel,
+ TupleTableSlot *slot);
+
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ scan_begin_function scan_begin;
+ scan_begin_catalog_function scan_begin_catalog;
+ scan_begin_strat_function scan_begin_strat;
+ scan_begin_bm_function scan_begin_bm;
+ scan_begin_sampling_function scan_begin_sampling;
+ scan_setlimits_function scansetlimits;
+ scan_getnext_function scan_getnext;
+ scan_end_function scan_end;
+ scan_getpage_function scan_getpage;
+ scan_rescan_function scan_rescan;
+ scan_rescan_set_params_function scan_rescan_set_params;
+ scan_update_snapshot_function scan_update_snapshot;
+
+ // heap_sync_function heap_sync; /* heap_sync */
+ /* not implemented */
+ // parallelscan_estimate_function parallelscan_estimate; /* heap_parallelscan_estimate */
+ // parallelscan_initialize_function parallelscan_initialize; /* heap_parallelscan_initialize */
+ // parallelscan_begin_function parallelscan_begin; /* heap_beginscan_parallel */
+
+ /* Operations on physical tuples */
+ tuple_insert_function tuple_insert; /* heap_insert */
+ tuple_update_function tuple_update; /* heap_update */
+ tuple_delete_function tuple_delete; /* heap_delete */
+ tuple_fetch_function tuple_fetch; /* heap_fetch */
+ tuple_lock_function tuple_lock; /* heap_lock_tuple */
+
+ tuple_get_xmin_function tuple_get_xmin; /* HeapTupleHeaderGetXmin */
+ tuple_get_updated_xid_function tuple_get_updated_xid;
+ tuple_get_cmin_function tuple_get_cmin; /* HeapTupleHeaderGetCmin */
+ tuple_get_itempointer_function tuple_get_itempointer;
+ tuple_get_ctid_function tuple_get_ctid;
+ tuple_is_heaponly_function tuple_is_heaponly;
+ tuple_from_datum_function tuple_from_datum;
+
+ /* Operations on TupleTableSlot */
+ slot_store_tuple_function slot_store_tuple;
+ slot_getattr_function slot_getattr;
+ slot_getallattrs_function slot_getallattrs;
+ slot_getsomeattrs_function slot_getsomeattrs;
+ slot_attisnull_function slot_attisnull;
+ slot_clear_tuple_function slot_clear_tuple;
+ slot_materialize_tuple_function slot_materialize_tuple;
+ slot_copy_tuple_function slot_copy_tuple;
+ slot_get_tuple_function slot_get_tuple;
+ slot_copy_min_tuple_function slot_copy_min_tuple;
+ slot_is_physical_tuple_function slot_is_physical_tuple;
+ slot_update_tableoid_function slot_update_tableoid;
+ slot_set_tuple_oid_function slot_set_tuple_oid;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no need
+ * for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ tuple_set_speculative_token_function tuple_set_speculative_token; /* HeapTupleHeaderSetSpeculativeToken */
+ speculative_finish_function speculative_finish;
+ speculative_abort_function speculative_abort;
+} StorageAmRoutine;
+
+extern StorageAmRoutine *GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine *GetStorageAmRoutineByAmId(Oid amoid);
+
+extern StorageAmRoutine *GetHeapamStorageAmRoutine(void);
+
+extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
+
+#endif /* STORAGEAMAPI_H */
--
2.7.4.windows.1
[application/octet-stream] 0004-Heap-storage-handler.patch (36.9K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/7-0004-Heap-storage-handler.patch)
download | inline diff:
From 63ee9cfc5076c1fe0bc36b1d619f20f8133fb7a2 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Thu, 1 Jun 2017 15:41:01 +1000
Subject: [PATCH 04/10] Heap storage handler
Adding the handler function in the relation
and add other required function hooks to support the
heap storage
---
src/backend/access/heap/heapam.c | 510 +++++++++++++++++++++++++++++++++----
src/backend/utils/cache/relcache.c | 117 ++++++++-
src/include/access/heapam.h | 43 +---
src/include/catalog/pg_proc.h | 5 +
src/include/utils/rel.h | 12 +
src/include/utils/relcache.h | 2 +
6 files changed, 595 insertions(+), 94 deletions(-)
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e890e08..fe8f91f 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -45,6 +45,7 @@
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -66,6 +67,7 @@
#include "storage/smgr.h"
#include "storage/spin.h"
#include "storage/standby.h"
+#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -74,11 +76,21 @@
#include "utils/syscache.h"
#include "utils/tqual.h"
-
/* GUC variable */
bool synchronize_seqscans = true;
-
+static void heap_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+static HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+static HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+static HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+static HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+static HeapScanDesc heap_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
static HeapScanDesc heap_beginscan_internal(Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
@@ -89,7 +101,36 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool is_bitmapscan,
bool is_samplescan,
bool temp_snap);
+static StorageTuple heap_getnext(HeapScanDesc sscan, ScanDirection direction);
+static void heap_endscan(HeapScanDesc sscan);
+
+static void heapgetpage(HeapScanDesc scan, BlockNumber page);
+static void heap_rescan(HeapScanDesc scan, ScanKey key);
+static void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+static void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
+
+static HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+static HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+static HTSU_Result heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
@@ -124,6 +165,9 @@ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
+static void heapam_finish_speculative(Relation relation, TupleTableSlot *slot);
+static void heapam_abort_speculative(Relation relation, TupleTableSlot *slot);
+static void heap_finish_speculative(Relation relation, HeapTuple tuple);
/*
@@ -202,6 +246,358 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
(MultiXactStatusLock[(status)])
/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate,
+ ItemPointer iptr)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is completely
+ * bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, iptr);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ ItemPointer iptr)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, iptr);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ heap_finish_speculative(relation, stuple->hst_heaptuple);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ heap_abort_speculative(relation, stuple->hst_heaptuple);
+ slot->tts_speculativeToken = 0;
+}
+
+static TransactionId
+heapam_get_xmin(StorageTuple tuple)
+{
+ TransactionId xmin;
+
+ xmin = HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data);
+ return xmin;
+}
+
+static TransactionId
+heapam_get_updated_xid(StorageTuple tuple)
+{
+ TransactionId xid;
+
+ xid = HeapTupleHeaderGetUpdateXid(((HeapTuple)tuple)->t_data);
+ return xid;
+}
+
+static CommandId
+heapam_get_cmin(StorageTuple tuple)
+{
+ return HeapTupleHeaderGetCmin(((HeapTuple)tuple)->t_data);
+}
+
+static ItemPointer
+heapam_get_itempointer(StorageTuple tuple)
+{
+ return &(((HeapTuple)tuple)->t_self);
+}
+
+static ItemPointerData
+heapam_get_ctid(StorageTuple tuple)
+{
+ return ((HeapTuple)tuple)->t_data->t_ctid;
+}
+
+static bool
+heapam_tuple_is_heaopnly(StorageTuple tuple)
+{
+ return HeapTupleIsHeapOnly((HeapTuple)tuple);
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ return stuple->hst_heaptuple;
+}
+
+static bool
+heapam_is_physical_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ return (stuple->hst_heaptuple != &slot->tts_minhdr);
+}
+
+static void
+slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = ExecHeapifySlot(slot);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+slot_set_tuple_oid(TupleTableSlot *slot, Oid tupleoid)
+{
+ HeapTuple tuple;
+
+ tuple = ExecHeapifySlot(slot);
+ HeapTupleSetOid(tuple, tupleoid);
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *)palloc(sizeof(HeapamTuple));
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_shouldFree = shouldFree;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (stuple->hst_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(slot->tts_mintuple);
+
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+static HeapTuple
+heapam_copy_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+ return heap_copytuple(stuple->hst_heaptuple);
+}
+
+static MinimalTuple
+minimal_tuple_from_heapam_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+ return minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+}
+
+static void
+heapam_tuple_set_speculative_token(TupleTableSlot *slot, uint32 specToken)
+{
+ HeapTuple tuple;
+
+ /*
+ * This implementation violates the contract of ExecFetchSlotTuple, which
+ * states that the returned tuple must be treated as read-only. Probably
+ * the way to fix this is to set the speculative token in the slot rather
+ * than the heaptuple directly, so that it can correctly be set on the
+ * tuple once it is materialized.
+ */
+ tuple = ExecFetchSlotTuple(slot);
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->scan_begin = heap_beginscan;
+ amroutine->scan_begin_catalog = heap_beginscan_catalog;
+ amroutine->scan_begin_strat = heap_beginscan_strat;
+ amroutine->scan_begin_bm = heap_beginscan_bm;
+ amroutine->scan_begin_sampling = heap_beginscan_sampling;
+ amroutine->scansetlimits = heap_setscanlimits;
+ amroutine->scan_getnext = heap_getnext;
+ amroutine->scan_end = heap_endscan;
+ //amroutine->scan_getpage = ;
+ amroutine->scan_rescan = heap_rescan;
+ amroutine->scan_rescan_set_params = heap_rescan_set_params;
+ amroutine->scan_update_snapshot = heap_update_snapshot;
+
+ amroutine->tuple_fetch = heap_fetch;
+
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heap_lock_tuple;
+
+ amroutine->tuple_get_cmin = heapam_get_cmin;
+ amroutine->tuple_get_xmin = heapam_get_xmin;
+ amroutine->tuple_get_updated_xid = heapam_get_updated_xid;
+ amroutine->tuple_get_itempointer = heapam_get_itempointer;
+ amroutine->tuple_get_ctid = heapam_get_ctid;
+ amroutine->tuple_is_heaponly = heapam_tuple_is_heaopnly;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_getattr = slot_getattr;
+ amroutine->slot_getallattrs = slot_getallattrs;
+ amroutine->slot_getsomeattrs = slot_getsomeattrs;
+ amroutine->slot_attisnull = slot_attisnull;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_copy_tuple = heapam_copy_tuple;
+ amroutine->slot_get_tuple = heapam_get_tuple;
+ amroutine->slot_copy_min_tuple = minimal_tuple_from_heapam_tuple;
+ amroutine->slot_is_physical_tuple = heapam_is_physical_tuple;
+ amroutine->slot_update_tableoid = slot_update_tuple_tableoid;
+ amroutine->slot_set_tuple_oid = slot_set_tuple_oid;
+
+
+ amroutine->tuple_set_speculative_token = heapam_tuple_set_speculative_token;
+ amroutine->speculative_finish = heapam_finish_speculative;
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ /* XXX more? */
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
*/
@@ -321,9 +717,11 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* startBlk is the page to start at
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
+static void
+heap_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(!scan->rs_inited); /* else too late to change */
Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
@@ -1387,7 +1785,7 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+static HeapScanDesc
heap_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -1395,7 +1793,7 @@ heap_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+static HeapScanDesc
heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -1405,7 +1803,7 @@ heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+static HeapScanDesc
heap_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -1415,7 +1813,7 @@ heap_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+static HeapScanDesc
heap_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -1423,7 +1821,7 @@ heap_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+static HeapScanDesc
heap_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -1574,7 +1972,7 @@ heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
* Check handling if reldesc caching.
* ----------------
*/
-void
+static void
heap_endscan(HeapScanDesc scan)
{
/* Note: no locking manipulations needed */
@@ -1790,9 +2188,11 @@ heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
#endif /* !defined(HEAPDEBUGALL) */
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
+static StorageTuple
+heap_getnext(HeapScanDesc sscan, ScanDirection direction)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
HEAPDEBUG_1; /* heap_getnext( info ) */
@@ -1858,20 +2258,21 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
* truncate off the destination page without having killed the referencing
* tuple first), but the item number might well not be good.
*/
-bool
+static bool
heap_fetch(Relation relation,
+ ItemPointer tid,
Snapshot snapshot,
- HeapTuple tuple,
+ StorageTuple *stuple,
Buffer *userbuf,
bool keep_buf,
Relation stats_relation)
{
- ItemPointer tid = &(tuple->t_self);
ItemId lp;
Buffer buffer;
Page page;
OffsetNumber offnum;
bool valid;
+ HeapTupleData tuple;
/*
* Fetch and pin the appropriate page of the relation.
@@ -1900,7 +2301,7 @@ heap_fetch(Relation relation,
ReleaseBuffer(buffer);
*userbuf = InvalidBuffer;
}
- tuple->t_data = NULL;
+ *stuple = NULL;
return false;
}
@@ -1922,26 +2323,27 @@ heap_fetch(Relation relation,
ReleaseBuffer(buffer);
*userbuf = InvalidBuffer;
}
- tuple->t_data = NULL;
+ *stuple = NULL;
return false;
}
/*
- * fill in *tuple fields
+ * fill in tuple fields and place it in stuple
*/
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &(tuple.t_self));
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(&tuple, snapshot, buffer);
if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
+ PredicateLockTuple(relation, &tuple, snapshot);
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+ CheckForSerializableConflictOut(valid, relation, &tuple, buffer, snapshot);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
@@ -1957,6 +2359,7 @@ heap_fetch(Relation relation,
if (stats_relation != NULL)
pgstat_count_heap_fetch(stats_relation);
+ *stuple = heap_copytuple(&tuple);
return true;
}
@@ -3007,7 +3410,7 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
* cannot obtain cmax from a combocid generated by another transaction).
* See comments for struct HeapUpdateFailureData for additional info.
*/
-HTSU_Result
+static HTSU_Result
heap_delete(Relation relation, ItemPointer tid,
CommandId cid, Snapshot crosscheck, bool wait,
HeapUpdateFailureData *hufd)
@@ -3458,7 +3861,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
* cannot obtain cmax from a combocid generated by another transaction).
* See comments for struct HeapUpdateFailureData for additional info.
*/
-HTSU_Result
+static HTSU_Result
heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
CommandId cid, Snapshot crosscheck, bool wait,
HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
@@ -4536,14 +4939,13 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
*
* See README.tuplock for a thorough explanation of this mechanism.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
+static HTSU_Result
+heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
Buffer *buffer, HeapUpdateFailureData *hufd)
{
HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
ItemId lp;
Page page;
Buffer vmbuffer = InvalidBuffer;
@@ -4556,6 +4958,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
bool first_time = true;
bool have_tuple_lock = false;
bool cleared_all_frozen = false;
+ HeapTuple tuple = (HeapTuple)stuple;
+
+ Assert(tuple != NULL);
*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
block = ItemPointerGetBlockNumber(tid);
@@ -5621,7 +6026,7 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
{
HTSU_Result result;
ItemPointerData tupid;
- HeapTupleData mytup;
+ HeapTuple mytup;
Buffer buf;
uint16 new_infomask,
new_infomask2,
@@ -5641,9 +6046,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
new_infomask = 0;
new_xmax = InvalidTransactionId;
block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
+ if (!heap_fetch(rel, &tupid, SnapshotAny, (StorageTuple *)&mytup, &buf, false, NULL))
{
/*
* if we fail to find the updated version of the tuple, it's
@@ -5689,7 +6093,7 @@ l4:
* end of the chain, we're done, so return success.
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup->t_data),
priorXmax))
{
result = HeapTupleMayBeUpdated;
@@ -5701,15 +6105,15 @@ l4:
* (sub)transaction, then we already locked the last live one in the
* chain, thus we're done, so return success.
*/
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup->t_data)))
{
UnlockReleaseBuffer(buf);
return HeapTupleMayBeUpdated;
}
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ old_infomask = mytup->t_data->t_infomask;
+ old_infomask2 = mytup->t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup->t_data);
/*
* If this tuple version has been updated or locked by some concurrent
@@ -5722,7 +6126,7 @@ l4:
TransactionId rawxmax;
bool needwait;
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup->t_data);
if (old_infomask & HEAP_XMAX_IS_MULTI)
{
int nmembers;
@@ -5752,7 +6156,7 @@ l4:
{
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
+ &mytup->t_self,
XLTW_LockUpdated);
pfree(members);
goto l4;
@@ -5811,7 +6215,7 @@ l4:
if (needwait)
{
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XactLockTableWait(rawxmax, rel, &mytup->t_self,
XLTW_LockUpdated);
goto l4;
}
@@ -5823,7 +6227,7 @@ l4:
}
/* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ compute_new_xmax_infomask(xmax, old_infomask, mytup->t_data->t_infomask2,
xid, mode, false,
&new_xmax, &new_infomask, &new_infomask2);
@@ -5835,11 +6239,11 @@ l4:
START_CRIT_SECTION();
/* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
+ HeapTupleHeaderSetXmax(mytup->t_data, new_xmax);
+ mytup->t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup->t_data->t_infomask |= new_infomask;
+ mytup->t_data->t_infomask2 |= new_infomask2;
MarkBufferDirty(buf);
@@ -5853,7 +6257,7 @@ l4:
XLogBeginInsert();
XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup->t_self);
xlrec.xmax = new_xmax;
xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
xlrec.flags =
@@ -5869,17 +6273,17 @@ l4:
END_CRIT_SECTION();
/* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ if (mytup->t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup->t_self, &mytup->t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup->t_data))
{
result = HeapTupleMayBeUpdated;
goto out_locked;
}
/* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup->t_data);
+ ItemPointerCopy(&(mytup->t_data->t_ctid), &tupid);
UnlockReleaseBuffer(buf);
if (vmbuffer != InvalidBuffer)
ReleaseBuffer(vmbuffer);
@@ -5959,7 +6363,7 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
* but clearing the token at completion isn't very expensive either.
* An explicit confirmation WAL record also makes logical decoding simpler.
*/
-void
+static void
heap_finish_speculative(Relation relation, HeapTuple tuple)
{
Buffer buffer;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index c2e8361..98dcc85 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1360,10 +1361,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1860,6 +1877,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2019,6 +2101,11 @@ formrdesc(const char *relationName, Oid relationReltype,
RelationInitPhysicalAddr(relation);
/*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
+ /*
* initialize the rel-has-index flag, using hardwired knowledge
*/
if (IsBootstrapProcessingMode())
@@ -2346,6 +2433,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3357,6 +3446,13 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3878,6 +3974,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert (relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5593,6 +5701,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 7e85510..962291e 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -100,41 +100,21 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
+/* struct definitions appear in storageamapi.h */
+typedef void *StorageScanDesc;
+typedef void *StorageTuple;
+
/*
* HeapScanIsValid
* True iff the heap scan is valid.
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
-
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
@@ -153,19 +133,6 @@ extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -179,7 +146,7 @@ extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
extern void heap_sync(Relation relation);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e232006..f6b899f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -549,6 +549,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 425 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 425
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8476896..c187c65 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -161,6 +161,12 @@ typedef struct RelationData
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
+ /*
* index access support info (used only for an index relation)
*
* Note: only default support procs for each opclass are cached, namely
@@ -429,6 +435,12 @@ typedef struct ViewOptions
#define RelationGetDescr(relation) ((relation)->rd_att)
/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
+/*
* RelationGetRelationName
* Returns the rel's name.
*
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 81af3ae..807345a 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.7.4.windows.1
[application/octet-stream] 0005-Adding-storageam-handler-to-slot.patch (22.8K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/8-0005-Adding-storageam-handler-to-slot.patch)
download | inline diff:
From 8bc00e8dc5e12caa6995b62346fd4c9f555eaba0 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Thu, 1 Jun 2017 22:01:38 +1000
Subject: [PATCH 05/10] Adding storageam handler to slot
Storage am handler to slot and also changing
the slot structure according to new access methods
---
src/backend/access/common/heaptuple.c | 25 +++--
src/backend/executor/execTuples.c | 172 +++++++++++++++++-----------------
src/include/access/htup_details.h | 12 +++
src/include/executor/tuptable.h | 50 ++++++----
src/include/nodes/nodes.h | 1 +
5 files changed, 145 insertions(+), 115 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c0086de..f04c870 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -1033,7 +1033,8 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
static void
slot_deform_tuple(TupleTableSlot *slot, int natts)
{
- HeapTuple tuple = slot->tts_tuple;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
Datum *values = slot->tts_values;
bool *isnull = slot->tts_isnull;
@@ -1060,8 +1061,8 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
else
{
/* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
}
tp = (char *) tup + tup->t_hoff;
@@ -1121,8 +1122,8 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
* Save state for next execution
*/
slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
}
/*
@@ -1140,7 +1141,8 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
HeapTupleHeader tup;
@@ -1236,6 +1238,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
+ HeapamTuple *stuple;
int tdesc_natts = slot->tts_tupleDescriptor->natts;
int attnum;
HeapTuple tuple;
@@ -1248,7 +1251,8 @@ slot_getallattrs(TupleTableSlot *slot)
* otherwise we had better have a physical tuple (tts_nvalid should equal
* natts in all virtual-tuple cases)
*/
- tuple = slot->tts_tuple;
+ stuple = (HeapamTuple *) slot->tts_storage;
+ tuple = stuple->hst_heaptuple;
if (tuple == NULL) /* internal error */
elog(ERROR, "cannot extract attribute from empty tuple slot");
@@ -1280,6 +1284,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
+ HeapamTuple *stuple;
HeapTuple tuple;
int attno;
@@ -1295,7 +1300,8 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
* otherwise we had better have a physical tuple (tts_nvalid should equal
* natts in all virtual-tuple cases)
*/
- tuple = slot->tts_tuple;
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
if (tuple == NULL) /* internal error */
elog(ERROR, "cannot extract attribute from empty tuple slot");
@@ -1327,7 +1333,8 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
/*
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index c4a9553..6912163 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -82,8 +82,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
@@ -113,16 +115,17 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageam = GetStorageAmRoutine(HEAPAM_STORAGE_AM_HANDLER_OID);
+ slot->tts_storage = NULL;
return slot;
}
@@ -247,6 +250,7 @@ void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc) /* new tuple descriptor */
{
+
/* For safety, make sure slot is empty before changing it */
ExecClearTuple(slot);
@@ -279,6 +283,30 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
}
/* --------------------------------
+ * ExecSetSlotStorageRoutine
+ *
+ * This function sets the storage AM routine pointer for the slot.
+ *
+ * XXX should this be part of ExecSetSlotDescriptor?
+ * --------------------------------
+ */
+void
+ExecSetSlotStorageRoutine(TupleTableSlot *slot,
+ StorageAmRoutine *routine)
+{
+ if (slot->tts_storageam)
+ {
+ /* XXX do we need any cleanup here? */
+ }
+
+ /*
+ * XXX maybe we need refcounts for StorageAmRoutine objects?
+ * or perhaps we need to copy here?
+ */
+ slot->tts_storageam = routine;
+}
+
+/* --------------------------------
* ExecStoreTuple
*
* This function is used to store a physical tuple into a specified
@@ -294,19 +322,6 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* on the buffer which is held until the slot is cleared, so that the tuple
* won't go away on us.
*
- * shouldFree is normally set 'true' for tuples constructed on-the-fly.
- * It must always be 'false' for tuples that are stored in disk pages,
- * since we don't want to try to pfree those.
- *
- * Another case where it is 'false' is when the referenced tuple is held
- * in a tuple table slot belonging to a lower-level executor Proc node.
- * In this case the lower-level slot retains ownership and responsibility
- * for eventually releasing the tuple. When this method is used, we must
- * be certain that the upper-level Proc node will lose interest in the tuple
- * sooner than the lower-level one does! If you're not certain, copy the
- * lower-level tuple with heap_copytuple and let the upper-level table
- * slot assume ownership of the copy!
- *
* Return value is just the passed-in slot pointer.
*
* NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple
@@ -317,7 +332,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +343,29 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageam->slot_store_tuple(slot, tuple, shouldFree);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,10 +392,7 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Drop the pin on the referenced buffer, if there is one.
@@ -412,9 +406,15 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
+
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageam->slot_store_tuple(slot, &slot->tts_minhdr, false);
+
slot->tts_mintuple = mtup;
slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
@@ -444,25 +444,20 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
+ slot->tts_storageam->slot_clear_tuple(slot);
/*
* Drop the pin on the referenced buffer, if there is one.
+ *
+ * XXX probably this belongs in the storage AM rather than here.
*/
if (BufferIsValid(slot->tts_buffer))
ReleaseBuffer(slot->tts_buffer);
slot->tts_buffer = InvalidBuffer;
+ slot->tts_mintuple = NULL;
/*
* Mark it empty.
@@ -541,7 +536,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -553,8 +548,8 @@ ExecCopySlotTuple(TupleTableSlot *slot)
/*
* If we have a physical tuple (either format) then just copy it.
*/
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
+ if (slot->tts_storage && slot->tts_storageam->slot_is_physical_tuple(slot))
+ return slot->tts_storageam->slot_copy_tuple(slot);
if (slot->tts_mintuple)
return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
@@ -588,8 +583,9 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
*/
if (slot->tts_mintuple)
return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+
+ if (slot->tts_storage)
+ return slot->tts_storageam->slot_copy_min_tuple(slot);
/*
* Otherwise we need to build a tuple from the Datum array.
@@ -614,7 +610,7 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
/*
@@ -624,15 +620,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
Assert(!slot->tts_isempty);
/*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
-
- /*
* Otherwise materialize the slot...
*/
- return ExecMaterializeSlot(slot);
+ return ExecHeapifySlot(slot);
}
/* --------------------------------
@@ -697,7 +687,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -713,18 +703,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ StorageTuple tuple;
/*
* sanity checks
@@ -732,12 +723,14 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
+
/*
* If we have a regular physical tuple, and it's locally palloc'd, we have
* nothing to do.
*/
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_storage &&
+ slot->tts_storageam->slot_is_physical_tuple(slot))
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,8 +740,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tuple = ExecCopySlotTuple(slot);
+ slot->tts_storageam->slot_store_tuple(slot, tuple, true);
MemoryContextSwitchTo(oldContext);
/*
@@ -777,8 +770,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
*/
if (!slot->tts_shouldFreeMin)
slot->tts_mintuple = NULL;
+}
+
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageam->slot_get_tuple(slot);
}
/* --------------------------------
@@ -794,7 +794,7 @@ ExecMaterializeSlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1107,11 +1107,11 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
}
/*
- * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
+ * BuildTupleFromCStrings - build a StorageTuple given user data in C string form.
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1119,7 +1119,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
@@ -1175,7 +1175,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
* for a composite Datum. However, we now require that composite Datums not
* contain any external TOAST pointers. We do not want heap_form_tuple itself
* to enforce that; more specifically, the rule applies only to actual Datums
- * and not to HeapTuple structures. Therefore, HeapTupleHeaderGetDatum is
+ * and not to StorageTuple structures. Therefore, HeapTupleHeaderGetDatum is
* now a function that detects whether there are externally-toasted fields
* and constructs a new tuple with inlined fields if so. We still need
* heap_form_tuple to insert the Datum header fields, because otherwise this
@@ -1183,7 +1183,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
*
* Note that if we do build a new tuple, it's palloc'd in the current
* memory context. Beware of code that changes context between the initial
- * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum.
+ * heap_form_tuple/etc call and calling StorageTuple(Header)GetDatum.
*
* For performance-critical callers, it could be worthwhile to take extra
* steps to ensure that there aren't TOAST pointers in the output of
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e365f4f..a8ab945 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -21,6 +21,18 @@
#include "storage/bufpage.h"
/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ bool hst_shouldFree;
+ long hst_off;
+} HeapamTuple;
+
+/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
* a tuple, plus the size of the null-values bitmap (at 1 bit per column),
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 32489ef..7521739 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -79,15 +96,6 @@
* mechanism to do more. However, the slot will increment the tupdesc
* reference count if a reference-counted tupdesc is supplied.)
*
- * When tts_shouldFree is true, the physical tuple is "owned" by the slot
- * and should be freed when the slot's reference to the tuple is dropped.
- *
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
- *
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
* to the descriptor's natts. When the slot is holding a physical tuple
@@ -114,24 +122,22 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
+ Buffer tts_buffer;
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageAmRoutine *tts_storageam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -144,8 +150,11 @@ extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
-extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
+ TupleDesc tupdesc);
+extern void ExecSetSlotStorageRoutine(TupleTableSlot *slot,
+ struct StorageAmRoutine *routine);
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -161,6 +170,7 @@ extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719..ac3375e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -496,6 +496,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
--
2.7.4.windows.1
[application/octet-stream] 0006-HeapTuple-replace-with-StorageTuple.patch (83.1K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/9-0006-HeapTuple-replace-with-StorageTuple.patch)
download | inline diff:
From 3c993d1f59e89a1b0e537b4bc02e62d482bfe028 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 11:07:32 +1000
Subject: [PATCH 06/10] HeapTuple replace with StorageTuple
Replace most of the executor flow of HeapTuple
with StorageTuple and the necessary code around it.
---
src/backend/commands/trigger.c | 39 +++---
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execExprInterp.c | 16 +--
src/backend/executor/execIndexing.c | 10 +-
src/backend/executor/execMain.c | 113 ++++++++---------
src/backend/executor/execReplication.c | 50 ++++----
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 18 ++-
src/backend/executor/nodeForeignscan.c | 7 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 12 +-
src/backend/executor/nodeIndexonlyscan.c | 4 +-
src/backend/executor/nodeIndexscan.c | 16 +--
src/backend/executor/nodeLockRows.c | 22 ++--
src/backend/executor/nodeModifyTable.c | 193 ++++++++++++++++--------------
src/backend/executor/nodeSamplescan.c | 27 +++--
src/backend/executor/nodeSeqscan.c | 13 +-
src/backend/executor/nodeTidscan.c | 16 +--
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 ++--
src/backend/executor/tqueue.c | 22 ++--
src/include/access/htup_details.h | 2 +-
src/include/access/relscan.h | 3 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 7 +-
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 10 +-
src/include/executor/tqueue.h | 2 +-
src/include/executor/tuptable.h | 6 +-
src/include/funcapi.h | 2 +-
src/include/nodes/execnodes.h | 4 +-
32 files changed, 337 insertions(+), 323 deletions(-)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 0271788..8fa42d5 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
@@ -2200,7 +2201,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2263,14 +2264,18 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes)
+ TupleTableSlot *slot, List *recheckIndexes)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if (trigdesc &&
(trigdesc->trig_insert_after_row || trigdesc->trig_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple, recheckIndexes, NULL);
+ }
}
TupleTableSlot *
@@ -2278,7 +2283,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2621,7 +2626,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2663,7 +2668,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2768,7 +2773,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -2920,7 +2925,8 @@ GetTupleForTrigger(EState *estate,
*/
ltrmark:;
tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
+ test = relation->rd_stamroutine->tuple_lock(relation, tid,
+ &tuple,
estate->es_output_cid,
lockmode, LockWaitBlock,
false, &buffer, &hufd);
@@ -3742,8 +3748,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -3804,14 +3810,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
@@ -3819,10 +3824,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!rel->rd_stamroutine->tuple_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -3836,10 +3840,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!rel->rd_stamroutine->tuple_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 7337d21..953284c 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -539,7 +539,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index fed0052..1217304 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2057,7 +2057,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
econtext);
/*
- * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+ * heap_attisnull needs a StorageTuple not a bare HeapTupleHeader.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
@@ -2368,7 +2368,7 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
void
ExecEvalRow(ExprState *state, ExprEvalStep *op)
{
- HeapTuple tuple;
+ HeapTuple tuple; //hari
/* build tuple from evaluated field values */
tuple = heap_form_tuple(op->d.row.tupdesc,
@@ -2497,7 +2497,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
format_type_be(attr->atttypid),
format_type_be(op->d.fieldselect.resulttype))));
- /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+ /* heap_getattr needs a StorageTuple not a bare HeapTupleHeader */
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
@@ -2540,7 +2540,7 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
else
{
/*
- * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+ * heap_deform_tuple needs a StorageTuple not a bare HeapTupleHeader. We
* set all the fields in the struct just in case.
*/
Datum tupDatum = *op->resvalue;
@@ -2566,7 +2566,7 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
void
ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
{
- HeapTuple tuple;
+ HeapTuple tuple; //hari
/* argdesc should already be valid from the DeForm step */
tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
@@ -2781,7 +2781,7 @@ void
ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
{
ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
- HeapTuple result;
+ HeapTuple result; //hari
Datum tupDatum;
HeapTupleHeader tuple;
HeapTupleData tmptup;
@@ -2840,7 +2840,7 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
MemoryContextSwitchTo(old_cxt);
}
- /* Following steps need a HeapTuple not a bare HeapTupleHeader */
+ /* Following steps need a StorageTuple not a bare HeapTupleHeader */
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
@@ -3345,7 +3345,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
TupleDesc output_tupdesc;
MemoryContext oldcontext;
HeapTupleHeader dtuple;
- HeapTuple tuple;
+ HeapTuple tuple; //hari
/* This was checked by ExecInitExpr */
Assert(variable->varattno == InvalidAttrNumber);
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 108060a..fe4c873 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -659,6 +659,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
ExprContext *econtext;
TupleTableSlot *existing_slot;
TupleTableSlot *save_scantuple;
+ StorageAmRoutine *method;
if (indexInfo->ii_ExclusionOps)
{
@@ -711,6 +712,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
econtext = GetPerTupleExprContext(estate);
save_scantuple = econtext->ecxt_scantuple;
econtext->ecxt_scantuple = existing_slot;
+ method = heap->rd_stamroutine;
/*
* May have to restart scan from this point if a potential conflict is
@@ -737,7 +739,7 @@ retry:
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, method->tuple_get_itempointer(tup)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +787,7 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ ctid_wait = method->tuple_get_ctid(tup);
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,7 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ *conflictTid = *(method->tuple_get_itempointer(tup));
break;
}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4a899f1..3614513 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1918,7 +1918,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -1964,7 +1964,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2006,7 +2006,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2437,7 +2437,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ StorageAmRoutine *method;
Assert(rti > 0);
@@ -2450,11 +2451,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
if (copyTuple == NULL)
return NULL;
+ method = relation->rd_stamroutine;
+
/*
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ *tid = *(method->tuple_get_itempointer(copyTuple));
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2485,7 +2489,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2518,13 +2522,13 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageAmRoutine *method = relation->rd_stamroutine;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
/*
@@ -2533,12 +2537,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (method->tuple_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2552,8 +2556,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ if (!TransactionIdEquals(method->tuple_get_xmin(tuple), priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2574,7 +2577,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2604,7 +2608,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* doing HeapTupleHeaderGetXmin again.
*/
if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ method->tuple_get_cmin(tuple) >= estate->es_output_cid)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2613,10 +2617,10 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, wait_policy,
- false, &buffer, &hufd);
+ test = method->tuple_lock(relation, tid, tuple,
+ estate->es_output_cid,
+ lockmode, wait_policy,
+ false, &buffer, &hufd);
/* We now have two pins on the buffer, get rid of one */
ReleaseBuffer(buffer);
@@ -2652,12 +2656,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ if (!ItemPointerEquals(&hufd.ctid, method->tuple_get_itempointer(tuple)))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2679,10 +2686,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2691,7 +2694,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2700,7 +2703,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ if (!TransactionIdEquals(method->tuple_get_xmin(tuple),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -2719,7 +2722,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ ctid = method->tuple_get_ctid(tuple);
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2727,17 +2731,18 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ priorXmax = method->tuple_get_updated_xid(tuple);
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2783,7 +2788,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2802,7 +2807,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2830,7 +2835,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2861,8 +2866,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2886,11 +2889,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
erm,
datum,
&updated);
- if (copyTuple == NULL)
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2904,23 +2907,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!erm->relation->rd_stamroutine->tuple_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2930,19 +2928,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = erm->relation->rd_stamroutine->tuple_from_datum(datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3108,8 +3099,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index c6a66b6..206238a 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -116,7 +116,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -169,12 +169,14 @@ retry:
HeapUpdateFailureData hufd;
HTSU_Result res;
HeapTupleData locktup;
+ ItemPointer tid;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ tid = outslot->tts_storageam->tuple_get_itempointer(outslot->tts_storage);
+ ItemPointerCopy(tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = rel->rd_stamroutine->tuple_lock(rel, tid, &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
@@ -219,7 +221,7 @@ retry:
* to use.
*/
static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
+tuple_equals_slot(TupleDesc desc, StorageTuple tup, TupleTableSlot *slot)
{
Datum values[MaxTupleAttributeNumber];
bool isnull[MaxTupleAttributeNumber];
@@ -267,7 +269,7 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
TransactionId xwait;
@@ -316,12 +318,14 @@ retry:
HeapUpdateFailureData hufd;
HTSU_Result res;
HeapTupleData locktup;
+ ItemPointer tid;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ tid = outslot->tts_storageam->tuple_get_itempointer(outslot->tts_storage);
+ ItemPointerCopy(tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = rel->rd_stamroutine->tuple_lock(rel, tid, &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
@@ -364,7 +368,7 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -392,18 +396,18 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, rel->rd_stamroutine->tuple_get_itempointer(tuple),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes);
list_free(recheckIndexes);
@@ -421,9 +425,10 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = searchslot->tts_storageam->tuple_get_itempointer(searchslot->tts_storage);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -435,7 +440,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -451,21 +456,19 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && rel->rd_stamroutine->tuple_is_heaponly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, rel->rd_stamroutine->tuple_get_itempointer(tuple),
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes);
list_free(recheckIndexes);
@@ -485,6 +488,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = searchslot->tts_storageam->tuple_get_itempointer(searchslot->tts_storage);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -496,7 +500,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -505,11 +509,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL);
+ tid, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index a35ba32..e2ab776 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -596,7 +596,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7eeda95..a412402 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3084,7 +3084,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3199,7 +3199,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index c453362..f8a4e7d 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -215,6 +215,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
+ //hari --> need a fix to change the scan
if (tbmres->blockno >= scan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
@@ -234,6 +235,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
+ //hari
scan->rs_cindex = 0;
/* Adjust the prefetch target */
@@ -244,6 +246,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
+ //hari
scan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -271,6 +274,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
+ //hari
if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
@@ -289,6 +293,8 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple
*/
+ //hari
+#if 1
targoffset = scan->rs_vistuples[scan->rs_cindex];
dp = (Page) BufferGetPage(scan->rs_cbuf);
lp = PageGetItemId(dp, targoffset);
@@ -309,7 +315,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
slot,
scan->rs_cbuf,
false);
-
+#endif
/*
* If we are using lossy info, we have to recheck the qual conditions
* at every tuple.
@@ -352,7 +358,8 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
Buffer buffer;
Snapshot snapshot;
int ntup;
-
+ //hari
+#if 1
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
@@ -394,11 +401,11 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
{
OffsetNumber offnum = tbmres->offsets[curslot];
ItemPointerData tid;
- HeapTupleData heapTuple;
+ HeapTupleData StorageTuple;
ItemPointerSet(&tid, page, offnum);
if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
- &heapTuple, NULL, true))
+ &StorageTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
@@ -440,6 +447,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
Assert(ntup <= MaxHeapTuplesPerPage);
scan->rs_ntuples = ntup;
+#endif
}
/*
@@ -588,6 +596,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
break;
}
node->prefetch_pages++;
+ //hari
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
}
}
@@ -630,6 +639,7 @@ BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
break;
}
+ //hari
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre->blockno);
}
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9ae1561..424acac 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -61,11 +61,8 @@ ForeignNext(ForeignScanState *node)
* tableoid, which is the only actually-useful system column.
*/
if (plan->fsSystemCol && !TupIsNull(slot))
- {
- HeapTuple tup = ExecMaterializeSlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
- }
+ slot->tts_storageam->slot_update_tableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
return slot;
}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index c1db2e2..cae003b 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -44,7 +44,7 @@
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -243,7 +243,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->reader != NULL || gatherstate->need_to_scan_locally)
{
@@ -284,7 +284,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -292,7 +292,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index e066574..1f98939 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -31,7 +31,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple;
+ StorageTuple *tuple;
int readCounter;
int nTuples;
bool done;
@@ -46,7 +46,7 @@ typedef struct GMReaderTupleBuffer
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
bool nowait, bool *done);
static void gather_merge_init(GatherMergeState *gm_state);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
@@ -369,7 +369,7 @@ gather_merge_init(GatherMergeState *gm_state)
{
/* Allocate the tuple array with MAX_TUPLE_STORE size */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize slot for worker */
gm_state->gm_slots[i] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -531,7 +531,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup = NULL;
+ StorageTuple tup = NULL;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -606,12 +606,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given reader.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup = NULL;
+ StorageTuple tup = NULL;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5550f6c..a5052de 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -115,7 +115,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
/*
* We can skip the heap fetch if the TID references a heap page on
@@ -182,7 +182,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
+ * provided in either StorageTuple or IndexTuple format. Conceivably an
* index AM might fill both fields, in which case we prefer the heap
* format, since it's probably a bit cheaper to fill a slot from.
*/
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 5afd02e..b936c13 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -49,7 +49,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -63,9 +63,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -82,7 +82,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -181,7 +181,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -475,7 +475,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -508,10 +508,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 5630eae..d628a40 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -70,7 +70,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
HeapTupleData tuple;
@@ -78,10 +78,10 @@ lnext:
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple)(&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -176,7 +176,7 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
+ test = erm->relation->rd_stamroutine->tuple_lock(erm->relation, &tuple.t_self, &tuple,
estate->es_output_cid,
lockmode, erm->waitPolicy, true,
&buffer, &hufd);
@@ -234,7 +234,7 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ tuple.t_self = *(erm->relation->rd_stamroutine->tuple_get_itempointer(copyTuple));
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -276,7 +276,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -304,14 +304,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!erm->relation->rd_stamroutine->tuple_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -389,8 +387,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index cf555fe..58827e0 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,6 +38,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -163,15 +164,14 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ econtext->ecxt_scantuple->tts_storageam->slot_update_tableoid(
+ econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -189,7 +189,8 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
- HeapTuple tuple,
+ Relation rel,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -208,7 +209,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(rel->rd_stamroutine->tuple_get_xmin(tuple)))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,16 +227,15 @@ ExecCheckTIDVisible(EState *estate,
{
Relation rel = relinfo->ri_RelationDesc;
Buffer buffer;
- HeapTupleData tuple;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!rel->rd_stamroutine->tuple_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -257,21 +257,16 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
+ StorageAmRoutine *method;
Oid newId;
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
/*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecMaterializeSlot(slot);
-
- /*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
@@ -282,6 +277,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -337,21 +334,35 @@ ExecInsert(ModifyTableState *mtstate,
}
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ method = resultRelationDesc->rd_stamroutine;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where the
+ * plan tree can return a tuple extracted literally from some table with
+ * the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_storageam->slot_set_tuple_oid(slot, InvalidOid);
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -369,9 +380,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -383,9 +391,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -401,14 +406,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_storageam->slot_update_tableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -418,7 +421,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_storageam->slot_update_tableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -440,6 +444,7 @@ ExecInsert(ModifyTableState *mtstate,
{
/* Perform a speculative insertion. */
uint32 specToken;
+ ItemPointerData newtid;
ItemPointerData conflictTid;
bool specConflict;
@@ -502,24 +507,24 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ method->tuple_set_speculative_token(slot, specToken);
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- HEAP_INSERT_SPECULATIVE,
- NULL);
+ newId = method->tuple_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ HEAP_INSERT_SPECULATIVE,
+ NULL, &newtid);
/* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &newtid,
estate, true, &specConflict,
arbiterIndexes);
/* adjust the tuple's state accordingly */
if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
+ method->speculative_finish(resultRelationDesc, slot);
else
- heap_abort_speculative(resultRelationDesc, tuple);
+ method->speculative_abort(resultRelationDesc, slot);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -545,19 +550,18 @@ ExecInsert(ModifyTableState *mtstate,
}
else
{
+ ItemPointerData iptr;
+
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- 0, NULL);
+ newId = method->tuple_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ 0, NULL, &iptr);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &iptr,
estate, false, NULL,
arbiterIndexes);
}
@@ -567,11 +571,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(method->tuple_get_itempointer(tuple));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes);
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes);
list_free(recheckIndexes);
@@ -620,7 +624,7 @@ ExecInsert(ModifyTableState *mtstate,
*/
static TupleTableSlot *
ExecDelete(ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -631,12 +635,14 @@ ExecDelete(ItemPointer tupleid,
HTSU_Result result;
HeapUpdateFailureData hufd;
TupleTableSlot *slot = NULL;
+ StorageAmRoutine *method;
/*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ method = resultRelationDesc->rd_stamroutine;
/* BEFORE ROW DELETE Triggers */
if (resultRelInfo->ri_TrigDesc &&
@@ -665,8 +671,6 @@ ExecDelete(ItemPointer tupleid,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -676,7 +680,8 @@ ExecDelete(ItemPointer tupleid,
*/
slot = estate->es_trig_tuple_slot;
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
+ ExecSetSlotDescriptor(slot,
+ RelationGetDescr(resultRelationDesc));
slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
resultRelInfo,
@@ -692,8 +697,10 @@ ExecDelete(ItemPointer tupleid,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -707,11 +714,11 @@ ExecDelete(ItemPointer tupleid,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd);
+ result = method->tuple_delete(resultRelationDesc, tupleid,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -806,7 +813,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -820,20 +827,20 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!method->tuple_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecSetSlotDescriptor(slot,
+ RelationGetDescr(resultRelationDesc));
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -878,16 +885,17 @@ ldelete:;
*/
static TupleTableSlot *
ExecUpdate(ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
+ StorageAmRoutine *method;
HTSU_Result result;
HeapUpdateFailureData hufd;
List *recheckIndexes = NIL;
@@ -902,13 +910,14 @@ ExecUpdate(ItemPointer tupleid,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ method = resultRelationDesc->rd_stamroutine;
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc &&
@@ -921,7 +930,7 @@ ExecUpdate(ItemPointer tupleid,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -935,7 +944,7 @@ ExecUpdate(ItemPointer tupleid,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -950,24 +959,25 @@ ExecUpdate(ItemPointer tupleid,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_storageam->slot_update_tableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
LockTupleMode lockmode;
+ ItemPointerData newtid;
/*
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1003,11 +1013,11 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd, &lockmode);
+ result = method->tuple_update(resultRelationDesc, tupleid, slot,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd, &lockmode, &newtid);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -1045,6 +1055,7 @@ lreplace:;
return NULL;
case HeapTupleMayBeUpdated:
+ ItemPointerCopy(method->tuple_get_itempointer(tuple), &newtid);
break;
case HeapTupleUpdated:
@@ -1067,7 +1078,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
@@ -1095,8 +1106,8 @@ lreplace:;
*
* If it's a HOT update, we mustn't insert new index entries.
*/
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && !method->tuple_is_heaponly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, method->tuple_get_itempointer(tuple),
estate, false, NULL, NIL);
}
@@ -1168,7 +1179,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* true anymore.
*/
tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
+ test = relation->rd_stamroutine->tuple_lock(relation, conflictTid, &tuple, estate->es_output_cid,
lockmode, LockWaitBlock, false, &buffer,
&hufd);
switch (test)
@@ -1254,7 +1265,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
@@ -1416,7 +1427,7 @@ ExecModifyTable(ModifyTableState *node)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
/*
* This should NOT get called during EvalPlanQual; we should have passed a
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 0247bd2..51fb182 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+ HeapScanDesc scan); //hari
/* ----------------------------------------------------------------
* Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -125,7 +126,7 @@ InitScanRelation(SampleScanState *node, EState *estate, int eflags)
node->ss.ss_currentRelation = currentRelation;
- /* we won't set up the HeapScanDesc till later */
+ /* we won't set up the StorageScanDesc till later */
node->ss.ss_currentScanDesc = NULL;
/* and report the scan tuple slot's rowtype */
@@ -342,7 +343,7 @@ tablesample_init(SampleScanState *scanstate)
/* We'll use syncscan if there's no NextSampleBlock function */
allow_sync = (tsm->NextSampleBlock == NULL);
- /* Now we can create or reset the HeapScanDesc */
+ /* Now we can create or reset the StorageScanDesc */
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
@@ -373,12 +374,12 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ HeapScanDesc scan = scanstate->ss.ss_currentScanDesc; //hari
+ HeapTuple tuple = &(scan->rs_ctup); //hari
Snapshot snapshot = scan->rs_snapshot;
bool pagemode = scan->rs_pageatatime;
BlockNumber blockno;
@@ -409,7 +410,7 @@ tablesample_getnext(SampleScanState *scanstate)
else
blockno = scan->rs_startblock;
Assert(blockno < scan->rs_nblocks);
- heapgetpage(scan, blockno);
+ //heapgetpage(scan, blockno); //hari
scan->rs_inited = true;
}
else
@@ -530,7 +531,7 @@ tablesample_getnext(SampleScanState *scanstate)
}
Assert(blockno < scan->rs_nblocks);
- heapgetpage(scan, blockno);
+ //heapgetpage(scan, blockno); //hari
/* Re-establish state for new page */
if (!pagemode)
@@ -551,7 +552,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 5680464..9cba6b3 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -48,8 +48,8 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ void *tuple;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -90,8 +90,9 @@ SeqNext(SeqScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
+ //hariscandesc->rs_cbuf, /* buffer associated with this
+ // * tuple */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -221,7 +222,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -266,7 +267,7 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 4860ec0..a97993c 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -305,7 +305,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -331,12 +331,6 @@ TidNext(TidScanState *node)
numTids = node->tss_NumTids;
/*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
- /*
* Initialize or advance scan position, depending on direction.
*/
bBackward = ScanDirectionIsBackward(direction);
@@ -363,7 +357,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -371,9 +365,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ heap_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (heapRelation->rd_stamroutine->tuple_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,7 +379,7 @@ TidNext(TidScanState *node)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 628bc9f..d1d73bf 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2087,7 +2087,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2155,7 +2155,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 97c3925..996f129 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -852,7 +852,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -960,7 +960,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1681,7 +1681,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1712,8 +1712,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 8d7e711..16adf26 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -192,12 +192,12 @@ static void TQSendRecordInfo(TQueueDestReceiver *tqueue, int32 typmod,
TupleDesc tupledesc);
static void TupleQueueHandleControlMessage(TupleQueueReader *reader,
Size nbytes, char *data);
-static HeapTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
+static StorageTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes, HeapTupleHeader data);
-static HeapTuple TQRemapTuple(TupleQueueReader *reader,
+static StorageTuple TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple);
+ StorageTuple tuple);
static Datum TQRemap(TupleQueueReader *reader, TupleRemapInfo *remapinfo,
Datum value, bool *changed);
static Datum TQRemapArray(TupleQueueReader *reader, ArrayRemapInfo *remapinfo,
@@ -225,7 +225,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
{
TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self;
TupleDesc tupledesc = slot->tts_tupleDescriptor;
- HeapTuple tuple;
+ HeapTuple tuple; //hari
shm_mq_result result;
/*
@@ -305,7 +305,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
}
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
@@ -675,7 +675,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
shm_mq_result result;
@@ -730,7 +730,7 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
/*
* Handle a data message - that is, a tuple - from the remote side.
*/
-static HeapTuple
+static StorageTuple
TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes,
HeapTupleHeader data)
@@ -759,11 +759,11 @@ TupleQueueHandleDataMessage(TupleQueueReader *reader,
/*
* Copy the given tuple, remapping any transient typmods contained in it.
*/
-static HeapTuple
+static StorageTuple
TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple)
+ StorageTuple tuple)
{
Datum *values;
bool *isnull;
@@ -1011,7 +1011,7 @@ TQRemapRecord(TupleQueueReader *reader, RecordRemapInfo *remapinfo,
if (changed_typmod || remapinfo->field_remap != NULL)
{
HeapTupleData htup;
- HeapTuple atup;
+ HeapTuple atup; //hari
/* For now, assume we always need to change the tuple in this case. */
*changed = true;
@@ -1121,7 +1121,7 @@ TupleQueueHandleControlMessage(TupleQueueReader *reader, Size nbytes,
static TupleRemapInfo *
BuildTupleRemapInfo(Oid typid, MemoryContext mycontext)
{
- HeapTuple tup;
+ StorageTuple tup;
Form_pg_type typ;
/* This is recursive, so it could be driven to stack overflow. */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index a8ab945..1802a97 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -665,7 +665,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index f4d4f1e..62013dc 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/itup.h"
+#include "access/storageamapi.h"
#include "access/tupdesc.h"
#include "storage/spin.h"
@@ -75,7 +76,7 @@ typedef struct HeapScanDescData
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
-} HeapScanDescData;
+} HeapScanDescData;
/*
* We use the same IndexScanDescData structure for both amgettuple-based
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index d73969c..5e79ed0 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -138,7 +138,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 8cc5f3a..0333df2 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -15,6 +15,7 @@
#define EXECUTOR_H
#include "catalog/partition.h"
+#include "access/storageamapi.h"
#include "executor/execdesc.h"
#include "nodes/parsenodes.h"
@@ -196,7 +197,7 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +205,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
PartitionDispatch **pd,
ResultRelInfo **partitions,
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 7821a63..fe0ce3f 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index ffb4c28..4031344 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index 892eec8..03282a9 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -27,7 +27,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle,
TupleDesc tupledesc);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 7521739..546d5e3 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -164,12 +164,12 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 30e66b6..b1593b0 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d33392f..b8b2d6a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -480,7 +480,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -1967,7 +1967,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
2.7.4.windows.1
[application/octet-stream] 0007-Replace-slot-functions-with-storage-access-methods.patch (22.9K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/10-0007-Replace-slot-functions-with-storage-access-methods.patch)
download | inline diff:
From 13ed2713b82306d32b1bf2265a90386efde677ce Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 11:15:31 +1000
Subject: [PATCH 07/10] Replace slot functions with storage access methods
Replace all slot_* function with slot storage access
methods
---
src/backend/access/common/printsimple.c | 3 ++-
src/backend/access/common/printtup.c | 9 +++++----
src/backend/catalog/index.c | 2 +-
src/backend/catalog/partition.c | 2 +-
src/backend/commands/copy.c | 15 +++++++--------
src/backend/executor/execCurrent.c | 4 ++--
src/backend/executor/execExprInterp.c | 9 ++++++---
src/backend/executor/execGrouping.c | 10 +++++-----
src/backend/executor/execJunk.c | 4 ++--
src/backend/executor/execMain.c | 5 +++--
src/backend/executor/functions.c | 2 +-
src/backend/executor/nodeAgg.c | 8 ++++----
src/backend/executor/nodeFunctionscan.c | 2 +-
src/backend/executor/nodeMergeAppend.c | 4 ++--
src/backend/executor/nodeNestloop.c | 2 +-
src/backend/executor/nodeSetOp.c | 2 +-
src/backend/executor/nodeSubplan.c | 12 ++++++------
src/backend/executor/tqueue.c | 3 ++-
src/backend/executor/tstoreReceiver.c | 3 ++-
src/backend/replication/logical/worker.c | 8 ++++++--
src/backend/utils/adt/orderedsetaggs.c | 4 ++--
21 files changed, 62 insertions(+), 51 deletions(-)
diff --git a/src/backend/access/common/printsimple.c b/src/backend/access/common/printsimple.c
index 851c3bf..7ab9b43 100644
--- a/src/backend/access/common/printsimple.c
+++ b/src/backend/access/common/printsimple.c
@@ -19,6 +19,7 @@
#include "postgres.h"
#include "access/printsimple.h"
+#include "access/storageamapi.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "libpq/pqformat.h"
@@ -63,7 +64,7 @@ printsimple(TupleTableSlot *slot, DestReceiver *self)
int i;
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* Prepare and send message */
pq_beginmessage(&buf, 'D');
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index a2ca2d7..0e5caa9 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "access/printtup.h"
+#include "access/storageamapi.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "tcop/pquery.h"
@@ -310,7 +311,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
printtup_prepare_info(myState, typeinfo, natts);
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
@@ -397,7 +398,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
printtup_prepare_info(myState, typeinfo, natts);
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
@@ -542,7 +543,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
for (i = 0; i < natts; ++i)
{
- attr = slot_getattr(slot, i + 1, &isnull);
+ attr = slot->tts_storageam->slot_getattr(slot, i + 1, &isnull);
if (isnull)
continue;
getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
@@ -583,7 +584,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
printtup_prepare_info(myState, typeinfo, natts);
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2328b92..558dbbc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1794,7 +1794,7 @@ FormIndexDatum(IndexInfo *indexInfo,
* Plain index column; get the value we need directly from the
* heap tuple.
*/
- iDatum = slot_getattr(slot, keycol, &isNull);
+ iDatum = slot->tts_storageam->slot_getattr(slot, keycol, &isNull);
}
else
{
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 7304f6c..3fe5e0a 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1884,7 +1884,7 @@ FormPartitionKeyDatum(PartitionDispatch pd,
if (keycol != 0)
{
/* Plain column; get the value directly from the heap tuple */
- datum = slot_getattr(slot, keycol, &isNull);
+ datum = slot->tts_storageam->slot_getattr(slot, keycol, &isNull);
}
else
{
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 84b1a54..afa4cb1 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2297,6 +2297,7 @@ CopyFrom(CopyState cstate)
bool *nulls;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
+ StorageAmRoutine *method;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
ExprContext *econtext;
TupleTableSlot *myslot;
@@ -2357,6 +2358,7 @@ CopyFrom(CopyState cstate)
}
tupDesc = RelationGetDescr(cstate->rel);
+ method = cstate->rel->rd_stamroutine;
/*----------
* Check to see if we can avoid writing WAL
@@ -2626,8 +2628,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
}
if (!skip_tuple)
@@ -2675,8 +2675,7 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ method->tuple_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, bistate, &(tuple->t_self));
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
@@ -2687,7 +2686,7 @@ CopyFrom(CopyState cstate)
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes);
list_free(recheckIndexes);
@@ -2840,7 +2839,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes);
list_free(recheckIndexes);
}
@@ -2857,7 +2856,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
{
cstate->cur_lineno = firstBufferedLineNo + i;
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL);
}
}
@@ -4708,7 +4707,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
CopyState cstate = myState->cstate;
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* And send the data */
CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index 3af4a90..350b688 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -185,12 +185,12 @@ execCurrentOf(CurrentOfExpr *cexpr,
/* Use slot_getattr to catch any possible mistakes */
tuple_tableoid =
- DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot,
+ DatumGetObjectId(scanstate->ss_ScanTupleSlot->tts_storageam->slot_getattr(scanstate->ss_ScanTupleSlot,
TableOidAttributeNumber,
&lisnull));
Assert(!lisnull);
tuple_tid = (ItemPointer)
- DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot,
+ DatumGetPointer(scanstate->ss_ScanTupleSlot->tts_storageam->slot_getattr(scanstate->ss_ScanTupleSlot,
SelfItemPointerAttributeNumber,
&lisnull));
Assert(!lisnull);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1217304..14220dc 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -507,7 +507,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+ *op->resvalue = heap_getsysattr(innerslot->tts_storageam->slot_get_tuple(innerslot),
+ attnum,
innerslot->tts_tupleDescriptor,
op->resnull);
@@ -523,7 +524,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
+ *op->resvalue = heap_getsysattr(outerslot->tts_storageam->slot_get_tuple(outerslot),
+ attnum,
outerslot->tts_tupleDescriptor,
op->resnull);
@@ -539,7 +541,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+ *op->resvalue = heap_getsysattr(scanslot->tts_storageam->slot_get_tuple(scanslot),
+ attnum,
scanslot->tts_tupleDescriptor,
op->resnull);
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index 07c8852..0d33cfe 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -97,9 +97,9 @@ execTuplesMatch(TupleTableSlot *slot1,
bool isNull1,
isNull2;
- attr1 = slot_getattr(slot1, att, &isNull1);
+ attr1 = slot1->tts_storageam->slot_getattr(slot1, att, &isNull1);
- attr2 = slot_getattr(slot2, att, &isNull2);
+ attr2 = slot2->tts_storageam->slot_getattr(slot2, att, &isNull2);
if (isNull1 != isNull2)
{
@@ -167,12 +167,12 @@ execTuplesUnequal(TupleTableSlot *slot1,
bool isNull1,
isNull2;
- attr1 = slot_getattr(slot1, att, &isNull1);
+ attr1 = slot1->tts_storageam->slot_getattr(slot1, att, &isNull1);
if (isNull1)
continue; /* can't prove anything here */
- attr2 = slot_getattr(slot2, att, &isNull2);
+ attr2 = slot2->tts_storageam->slot_getattr(slot2, att, &isNull2);
if (isNull2)
continue; /* can't prove anything here */
@@ -498,7 +498,7 @@ TupleHashTableHash(struct tuplehash_hash *tb, const MinimalTuple tuple)
/* rotate hashkey left 1 bit at each step */
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
- attr = slot_getattr(slot, att, &isNull);
+ attr = slot->tts_storageam->slot_getattr(slot, att, &isNull);
if (!isNull) /* treat nulls as having hash key 0 */
{
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index a422327..08f7b7a 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -250,7 +250,7 @@ ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
{
Assert(attno > 0);
- return slot_getattr(slot, attno, isNull);
+ return slot->tts_storageam->slot_getattr(slot, attno, isNull);
}
/*
@@ -274,7 +274,7 @@ ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
/*
* Extract all the values of the old tuple.
*/
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
old_values = slot->tts_values;
old_isnull = slot->tts_isnull;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 3614513..b519cb0 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -38,6 +38,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -1903,7 +1904,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
for (attrChk = 1; attrChk <= natts; attrChk++)
{
if (tupdesc->attrs[attrChk - 1]->attnotnull &&
- slot_attisnull(slot, attrChk))
+ slot->tts_storageam->slot_attisnull(slot, attrChk))
{
char *val_desc;
Relation orig_rel = rel;
@@ -2220,7 +2221,7 @@ ExecBuildSlotValueDescription(Oid reloid,
table_perm = any_perm = true;
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
for (i = 0; i < tupdesc->natts; i++)
{
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index e2ab776..e7db610 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -971,7 +971,7 @@ postquel_get_single_result(TupleTableSlot *slot,
* Returning a scalar, which we have to extract from the first column
* of the SELECT result, and then copy into result context if needed.
*/
- value = slot_getattr(slot, 1, &(fcinfo->isnull));
+ value = slot->tts_storageam->slot_getattr(slot, 1, &(fcinfo->isnull));
if (!fcinfo->isnull)
value = datumCopy(value, fcache->typbyval, fcache->typlen);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index a412402..1fbd5ad 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1419,7 +1419,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
* transfn. (This will help execTuplesMatch too, so we do it
* immediately.)
*/
- slot_getsomeattrs(slot1, numTransInputs);
+ slot1->tts_storageam->slot_getsomeattrs(slot1, numTransInputs);
if (numDistinctCols == 0 ||
!haveOldValue ||
@@ -1683,7 +1683,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet
ListCell *lc;
/* all_grouped_cols is arranged in desc order */
- slot_getsomeattrs(slot, linitial_int(aggstate->all_grouped_cols));
+ slot->tts_storageam->slot_getsomeattrs(slot, linitial_int(aggstate->all_grouped_cols));
foreach(lc, aggstate->all_grouped_cols)
{
@@ -2032,7 +2032,7 @@ lookup_hash_entry(AggState *aggstate)
int i;
/* transfer just the needed columns into hashslot */
- slot_getsomeattrs(inputslot, perhash->largestGrpColIdx);
+ inputslot->tts_storageam->slot_getsomeattrs(inputslot, perhash->largestGrpColIdx);
ExecClearTuple(hashslot);
for (i = 0; i < perhash->numhashGrpCols; i++)
@@ -2608,7 +2608,7 @@ agg_retrieve_hash_table(AggState *aggstate)
* columns.
*/
ExecStoreMinimalTuple(entry->firstTuple, hashslot, false);
- slot_getallattrs(hashslot);
+ hashslot->tts_storageam->slot_getallattrs(hashslot);
ExecClearTuple(firstSlot);
memset(firstSlot->tts_isnull, true,
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 426527d..9d2de95 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -207,7 +207,7 @@ FunctionNext(FunctionScanState *node)
/*
* we have a result, so just copy it to the result cols.
*/
- slot_getallattrs(fs->func_slot);
+ fs->func_slot->tts_storageam->slot_getallattrs(fs->func_slot);
for (i = 0; i < fs->colcount; i++)
{
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index fef83db..501e5fc 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -249,8 +249,8 @@ heap_compare_slots(Datum a, Datum b, void *arg)
isNull2;
int compare;
- datum1 = slot_getattr(s1, attno, &isNull1);
- datum2 = slot_getattr(s2, attno, &isNull2);
+ datum1 = s1->tts_storageam->slot_getattr(s1, attno, &isNull1);
+ datum2 = s2->tts_storageam->slot_getattr(s2, attno, &isNull2);
compare = ApplySortComparator(datum1, isNull1,
datum2, isNull2,
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 69d2453..5874c74 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -133,7 +133,7 @@ ExecNestLoop(NestLoopState *node)
Assert(IsA(nlp->paramval, Var));
Assert(nlp->paramval->varno == OUTER_VAR);
Assert(nlp->paramval->varattno > 0);
- prm->value = slot_getattr(outerTupleSlot,
+ prm->value = outerTupleSlot->tts_storageam->slot_getattr(outerTupleSlot,
nlp->paramval->varattno,
&(prm->isnull));
/* Flag parameter value as changed */
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 9ae53bb..056e968 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -104,7 +104,7 @@ fetch_tuple_flag(SetOpState *setopstate, TupleTableSlot *inputslot)
int flag;
bool isNull;
- flag = DatumGetInt32(slot_getattr(inputslot,
+ flag = DatumGetInt32(inputslot->tts_storageam->slot_getattr(inputslot,
node->flagColIdx,
&isNull));
Assert(!isNull);
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e8fa4c8..4cf6870 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -358,7 +358,7 @@ ExecScanSubPlan(SubPlanState *node,
found = true;
/* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
- dvalue = slot_getattr(slot, 1, &disnull);
+ dvalue = slot->tts_storageam->slot_getattr(slot, 1, &disnull);
astate = accumArrayResultAny(astate, dvalue, disnull,
subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */
@@ -386,7 +386,7 @@ ExecScanSubPlan(SubPlanState *node,
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL);
- prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
+ prmdata->value = slot->tts_storageam->slot_getattr(slot, col, &(prmdata->isnull));
col++;
}
@@ -556,7 +556,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL);
- prmdata->value = slot_getattr(slot, col,
+ prmdata->value = slot->tts_storageam->slot_getattr(slot, col,
&(prmdata->isnull));
col++;
}
@@ -646,7 +646,7 @@ slotAllNulls(TupleTableSlot *slot)
for (i = 1; i <= ncols; i++)
{
- if (!slot_attisnull(slot, i))
+ if (!slot->tts_storageam->slot_attisnull(slot, i))
return false;
}
return true;
@@ -666,7 +666,7 @@ slotNoNulls(TupleTableSlot *slot)
for (i = 1; i <= ncols; i++)
{
- if (slot_attisnull(slot, i))
+ if (slot->tts_storageam->slot_attisnull(slot, i))
return false;
}
return true;
@@ -988,7 +988,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
found = true;
/* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
- dvalue = slot_getattr(slot, 1, &disnull);
+ dvalue = slot->tts_storageam->slot_getattr(slot, 1, &disnull);
astate = accumArrayResultAny(astate, dvalue, disnull,
subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 16adf26..7bef12e 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -35,6 +35,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "catalog/pg_type.h"
#include "executor/tqueue.h"
#include "funcapi.h"
@@ -265,7 +266,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
MemoryContext oldcontext = NULL;
/* Deform the tuple so we can examine fields, if not done already. */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/* Iterate over each attribute and search it for transient typmods. */
for (i = 0; i < tupledesc->natts; i++)
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index 1e641c9..1ac48b2 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -20,6 +20,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/tuptoaster.h"
#include "executor/tstoreReceiver.h"
@@ -116,7 +117,7 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
MemoryContext oldcxt;
/* Make sure the tuple is fully deconstructed */
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
/*
* Fetch back any out-of-line datums. We build the new datums array in
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 7d1787d..dabe87e 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -366,7 +366,7 @@ slot_modify_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
SlotErrCallbackArg errarg;
ErrorContextCallback errcallback;
- slot_getallattrs(slot);
+ slot->tts_storageam->slot_getallattrs(slot);
ExecClearTuple(slot);
/* Push callback + info on the error context stack */
@@ -700,6 +700,7 @@ apply_handle_update(StringInfo s)
found = RelationFindReplTupleSeq(rel->localrel, LockTupleExclusive,
remoteslot, localslot);
+ remoteslot->tts_storageam->slot_clear_tuple(remoteslot);
ExecClearTuple(remoteslot);
/*
@@ -709,9 +710,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ remoteslot->tts_storageam->slot_store_tuple(remoteslot, tuple, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 8502fcf..718b862 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -1193,7 +1193,7 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot, NULL))
{
bool isnull;
- Datum d = slot_getattr(slot, nargs + 1, &isnull);
+ Datum d = slot->tts_storageam->slot_getattr(slot, nargs + 1, &isnull);
if (!isnull && DatumGetInt32(d) != 0)
break;
@@ -1357,7 +1357,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
&abbrevVal))
{
bool isnull;
- Datum d = slot_getattr(slot, nargs + 1, &isnull);
+ Datum d = slot->tts_storageam->slot_getattr(slot, nargs + 1, &isnull);
TupleTableSlot *tmpslot;
if (!isnull && DatumGetInt32(d) != 0)
--
2.7.4.windows.1
[application/octet-stream] 0008-Replace-heap_-functions-with-storage-access-methods.patch (63.4K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/11-0008-Replace-heap_-functions-with-storage-access-methods.patch)
download | inline diff:
From 9041e7225262a3ab81e85a69288c2bdbd9a6f684 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 11:36:35 +1000
Subject: [PATCH 08/10] Replace heap_* functions with storage access methods
Replace all heap_* functions with storage access methods
in the entire code, but these are not yet removed the
HeapTuple and HeapScanDesc use.
---
contrib/pgrowlocks/pgrowlocks.c | 14 +++++---
contrib/pgstattuple/pgstattuple.c | 9 ++---
contrib/postgres_fdw/postgres_fdw.c | 2 +-
contrib/tsm_system_rows/tsm_system_rows.c | 8 ++---
contrib/tsm_system_time/tsm_system_time.c | 2 +-
src/backend/access/index/genam.c | 8 +++--
src/backend/bootstrap/bootstrap.c | 30 ++++++++++-------
src/backend/catalog/aclchk.c | 17 ++++++----
src/backend/catalog/index.c | 32 +++++++++++-------
src/backend/catalog/pg_conversion.c | 10 +++---
src/backend/catalog/pg_db_role_setting.c | 10 +++---
src/backend/catalog/pg_publication.c | 8 +++--
src/backend/catalog/pg_subscription.c | 7 ++--
src/backend/commands/cluster.c | 18 ++++++----
src/backend/commands/copy.c | 7 ++--
src/backend/commands/dbcommands.c | 26 +++++++++-----
src/backend/commands/indexcmds.c | 9 +++--
src/backend/commands/tablecmds.c | 42 +++++++++++++----------
src/backend/commands/tablespace.c | 54 +++++++++++++++++-------------
src/backend/commands/typecmds.c | 15 +++++----
src/backend/commands/vacuum.c | 18 ++++++----
src/backend/executor/execReplication.c | 12 ++++---
src/backend/executor/nodeBitmapHeapscan.c | 13 +++----
src/backend/executor/nodeSamplescan.c | 6 ++--
src/backend/executor/nodeSeqscan.c | 11 +++---
src/backend/postmaster/autovacuum.c | 23 ++++++++-----
src/backend/postmaster/pgstat.c | 9 +++--
src/backend/replication/logical/launcher.c | 8 +++--
src/backend/rewrite/rewriteDefine.c | 9 +++--
src/backend/utils/init/postinit.c | 9 +++--
30 files changed, 269 insertions(+), 177 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 00e2015..97ee00f 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -55,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -70,13 +71,16 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
Datum result;
MyData *mydata;
Relation rel;
+ StorageAmRoutine *method;
+
+ method = rel->rd_stamroutine;
if (SRF_IS_FIRSTCALL())
{
@@ -112,7 +116,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = method->scan_begin(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -128,7 +132,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -293,7 +297,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index eb02ec5..5ff67e4 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,7 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -322,15 +322,16 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = method->scan_begin_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -383,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ method->scan_end(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks *BLCKSZ;
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 080cb0a..498b812 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3219,7 +3219,7 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate,
Datum value;
bool isnull;
- value = slot_getattr(slot, attnum, &isnull);
+ value = slot->tts_storageam->slot_getattr(slot, attnum, &isnull);
if (isnull)
p_values[pindex] = NULL;
else
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458e..ed0078e 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, StorageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, StorageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025..4c32166 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
instr_time cur_time;
/* First call within scan? */
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index a91fda7..7b85395 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -387,6 +388,7 @@ systable_beginscan(Relation heapRelation,
}
else
{
+ StorageAmRoutine *method = heapRelation->rd_stamroutine;
/*
* We disallow synchronized scans when forced to use a heapscan on a
* catalog. In most cases the desired rows are near the front, so
@@ -394,7 +396,7 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
+ sysscan->scan = method->scan_begin_strat(heapRelation, snapshot,
nkeys, key,
true, false);
sysscan->iscan = NULL;
@@ -432,7 +434,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = sysscan->heap_rel->rd_stamroutine->scan_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +506,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ sysscan->heap_rel->rd_stamroutine->scan_end(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 4c28b2b..9b7241a 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
@@ -571,20 +572,23 @@ boot_openrel(char *relname)
if (Typ == NULL)
{
+ StorageAmRoutine *method;
+
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ method->scan_end(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = method->scan_begin_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -592,7 +596,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, NoLock);
}
@@ -877,6 +881,7 @@ gettype(char *type)
HeapScanDesc scan;
HeapTuple tup;
struct typmap **app;
+ StorageAmRoutine *method;
if (Typ != NULL)
{
@@ -898,25 +903,26 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ method->scan_end(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = method->scan_begin_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 387a3be..22ccd34 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -790,6 +791,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
+ StorageAmRoutine *method;
ScanKeyInit(&key[0],
Anum_pg_proc_pronamespace,
@@ -797,14 +799,15 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -831,6 +834,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
+ StorageAmRoutine *method;
ScanKeyInit(&key[0],
Anum_pg_class_relnamespace,
@@ -842,14 +846,15 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 558dbbc..42be25b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1892,16 +1892,18 @@ index_update_stats(Relation rel,
/* don't assume syscache will work */
HeapScanDesc pg_class_scan;
ScanKeyData key[1];
+ StorageAmRoutine *method;
ScanKeyInit(&key[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ method = pg_class->rd_stamroutine;
+ pg_class_scan = method->scan_begin_catalog(pg_class, 1, key);
+ tuple = method->scan_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ method->scan_end(pg_class_scan);
}
else
{
@@ -2216,6 +2218,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2271,7 +2274,8 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ method = heapRelation->rd_stamroutine;
+ scan = method->scan_begin_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -2280,7 +2284,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ method->scansetlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2293,7 +2297,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2605,7 +2609,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2649,6 +2653,7 @@ IndexCheckExclusion(Relation heapRelation,
EState *estate;
ExprContext *econtext;
Snapshot snapshot;
+ StorageAmRoutine *method = heapRelation->rd_stamroutine;
/*
* If we are reindexing the target index, mark it as no longer being
@@ -2676,14 +2681,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = method->scan_begin_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
true); /* syncscan OK */
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2719,7 +2724,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -2970,6 +2975,7 @@ validate_index_heapscan(Relation heapRelation,
ItemPointer indexcursor = NULL;
ItemPointerData decoded;
bool tuplesort_empty = false;
+ StorageAmRoutine *method = heapRelation->rd_stamroutine;
/*
* sanity checks
@@ -2996,7 +3002,7 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = method->scan_begin_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -3006,7 +3012,7 @@ validate_index_heapscan(Relation heapRelation,
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3163,7 +3169,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..01fb130 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -152,6 +153,7 @@ RemoveConversionById(Oid conversionOid)
HeapTuple tuple;
HeapScanDesc scan;
ScanKeyData scanKeyData;
+ StorageAmRoutine *method;
ScanKeyInit(&scanKeyData,
ObjectIdAttributeNumber,
@@ -160,15 +162,15 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
-
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = method->scan_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..c0adb61 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -174,9 +175,10 @@ DropSetting(Oid databaseid, Oid roleid)
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
+ StorageAmRoutine *method;
relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock);
-
+ method = relsetting->rd_stamroutine;
if (OidIsValid(databaseid))
{
ScanKeyInit(&keys[numkeys],
@@ -196,12 +198,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = method->scan_begin_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = method->scan_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 17105f4..f1eeccb 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -292,17 +292,19 @@ GetAllTablesPublicationRelations(void)
HeapScanDesc scan;
HeapTuple tuple;
List *result = NIL;
+ StorageAmRoutine *method;
classRel = heap_open(RelationRelationId, AccessShareLock);
+ method = classRel->rd_stamroutine;
ScanKeyInit(&key[0],
Anum_pg_class_relkind,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = method->scan_begin_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -311,7 +313,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index ab5f371..93d89d2 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -393,12 +394,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = rel->rd_stamroutine->scan_begin_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = rel->rd_stamroutine->scan_getnext(scan, ForwardScanDirection)))
{
simple_heap_delete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ rel->rd_stamroutine->scan_end(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ef1abf3..5786226 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -758,6 +759,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
tups_recently_dead = 0;
int elevel = verbose ? INFO : DEBUG2;
PGRUsage ru0;
+ StorageAmRoutine *method;
pg_rusage_init(&ru0);
@@ -771,6 +773,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
else
OldIndex = NULL;
+ method = OldHeap->rd_stamroutine;
+
/*
* Their tuple descriptors should be exactly alike, but here we only need
* assume that they have the same number of columns.
@@ -908,7 +912,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = method->scan_begin(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -958,7 +962,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = method->scan_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1044,7 +1048,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ method->scan_end(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1643,6 +1647,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContext old_context;
RelToCluster *rvtc;
List *rvs = NIL;
+ StorageAmRoutine *method;
/*
* Get all indexes that have indisclustered set and are owned by
@@ -1651,12 +1656,13 @@ get_tables_to_cluster(MemoryContext cluster_context)
* called with one of them as argument.
*/
indRelation = heap_open(IndexRelationId, AccessShareLock);
+ method = indRelation->rd_stamroutine;
ScanKeyInit(&entry,
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = method->scan_begin_catalog(indRelation, 1, &entry);
+ while ((indexTuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1676,7 +1682,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ method->scan_end(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index afa4cb1..810c0cd 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2042,14 +2042,15 @@ CopyTo(CopyState cstate)
bool *nulls;
HeapScanDesc scandesc;
HeapTuple tuple;
+ StorageAmRoutine *method = cstate->rel->rd_stamroutine;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = method->scan_begin(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2061,7 +2062,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 11038f6..ed19018 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -585,13 +586,16 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
PG_ENSURE_ERROR_CLEANUP(createdb_failure_callback,
PointerGetDatum(&fparms));
{
+ StorageAmRoutine *method;
+
/*
* Iterate through all tablespaces of the template database, and copy
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +647,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1873,10 +1877,12 @@ remove_dbtablespaces(Oid db_id)
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
+ StorageAmRoutine *method;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1923,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
}
@@ -1940,10 +1946,12 @@ check_db_file_conflict(Oid db_id)
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
+ StorageAmRoutine *method;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1974,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 4861799..d5554c5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1869,6 +1870,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
List *relids = NIL;
ListCell *l;
int num_keys;
+ StorageAmRoutine *method;
AssertArg(objectName);
Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
@@ -1935,8 +1937,9 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = relationRelation->rd_stamroutine;
+ scan = method->scan_begin_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1976,7 +1979,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fb961e4..8256145 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4424,6 +4424,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
List *dropped_attrs = NIL;
ListCell *lc;
Snapshot snapshot;
+ StorageAmRoutine *method = oldrel->rd_stamroutine;
if (newrel)
ereport(DEBUG1,
@@ -4477,7 +4478,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = method->scan_begin(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4485,7 +4486,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4579,7 +4580,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ newrel->rd_stamroutine->tuple_insert(newrel, newslot, mycid, hi_options, bistate, &tuple->t_self);
ResetExprContext(econtext);
@@ -4587,7 +4588,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -4969,17 +4970,18 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
HeapScanDesc scan;
HeapTuple tuple;
List *result = NIL;
+ StorageAmRoutine *method;
classRel = heap_open(RelationRelationId, AccessShareLock);
-
+ method = classRel->rd_stamroutine;
ScanKeyInit(&key[0],
Anum_pg_class_reloftype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = method->scan_begin_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -4991,7 +4993,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8090,6 +8092,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Form_pg_constraint constrForm;
bool isnull;
Snapshot snapshot;
+ StorageAmRoutine *method;
/*
* VALIDATE CONSTRAINT is a no-op for foreign tables and partitioned
@@ -8123,7 +8126,8 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8131,7 +8135,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8146,7 +8150,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8169,6 +8173,7 @@ validateForeignKeyConstraint(char *conname,
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
+ StorageAmRoutine *method;
ereport(DEBUG1,
(errmsg("validating foreign key constraint \"%s\"", conname)));
@@ -8201,9 +8206,10 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8232,7 +8238,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
}
@@ -10620,6 +10626,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
List *role_oids = roleSpecsToIds(stmt->roles);
+ StorageAmRoutine *method;
/* Ensure we were not asked to move something we can't */
if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
@@ -10680,8 +10687,9 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 1, key);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10740,7 +10748,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index f9c2620..ca7145e 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -406,18 +407,19 @@ DropTableSpace(DropTableSpaceStmt *stmt)
HeapTuple tuple;
ScanKeyData entry[1];
Oid tablespaceoid;
+ StorageAmRoutine *method;
/*
* Find the target tuple
*/
rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
-
+ method = rel->rd_stamroutine;
ScanKeyInit(&entry[0],
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = method->scan_begin_catalog(rel, 1, entry);
+ tuple = method->scan_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +436,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +463,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -917,16 +919,17 @@ RenameTableSpace(const char *oldname, const char *newname)
HeapTuple newtuple;
Form_pg_tablespace newform;
ObjectAddress address;
+ StorageAmRoutine *method;
/* Search pg_tablespace */
rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
-
+ method = rel->rd_stamroutine;
ScanKeyInit(&entry[0],
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = method->scan_begin_catalog(rel, 1, entry);
+ tup = method->scan_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +940,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ method->scan_end(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +958,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = method->scan_begin_catalog(rel, 1, entry);
+ tup = method->scan_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ method->scan_end(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -997,16 +1000,17 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
bool repl_null[Natts_pg_tablespace];
bool repl_repl[Natts_pg_tablespace];
HeapTuple newtuple;
+ StorageAmRoutine *method;
/* Search pg_tablespace */
rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
-
+ method = rel->rd_stamroutine;
ScanKeyInit(&entry[0],
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = method->scan_begin_catalog(rel, 1, entry);
+ tup = method->scan_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1051,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1384,6 +1388,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
HeapScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
+ StorageAmRoutine *method;
/*
* Search pg_tablespace. We use a heapscan here even though there is an
@@ -1391,13 +1396,13 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
* a few entries and so an indexed lookup is a waste of effort.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
-
+ method = rel->rd_stamroutine;
ScanKeyInit(&entry[0],
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = method->scan_begin_catalog(rel, 1, entry);
+ tuple = method->scan_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1410,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1430,6 +1435,7 @@ get_tablespace_name(Oid spc_oid)
HeapScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
+ StorageAmRoutine *method;
/*
* Search pg_tablespace. We use a heapscan here even though there is an
@@ -1437,13 +1443,13 @@ get_tablespace_name(Oid spc_oid)
* few entries and so an indexed lookup is a waste of effort.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
-
+ method = rel->rd_stamroutine;
ScanKeyInit(&entry[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = method->scan_begin_catalog(rel, 1, entry);
+ tuple = method->scan_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1457,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ method->scan_end(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c765e97..2c494cc 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2312,11 +2312,13 @@ AlterDomainNotNull(List *names, bool notNull)
HeapScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
+ StorageAmRoutine *method;
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = testrel->rd_stamroutine;
+ scan = method->scan_begin(testrel, snapshot, 0, NULL);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2344,7 +2346,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2707,11 +2709,12 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
HeapScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
+ StorageAmRoutine *method = testrel->rd_stamroutine;
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = method->scan_begin(testrel, snapshot, 0, NULL);
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2753,7 +2756,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 9fbb0eb..e30f270 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -444,12 +445,14 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
+ StorageAmRoutine *method;
pgclass = heap_open(RelationRelationId, AccessShareLock);
+ method = pgclass->rd_stamroutine;
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = method->scan_begin_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -469,7 +472,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(pgclass, AccessShareLock);
}
@@ -1096,6 +1099,7 @@ vac_truncate_clog(TransactionId frozenXID,
Oid minmulti_datoid;
bool bogus = false;
bool frozenAlreadyWrapped = false;
+ StorageAmRoutine *method;
/* init oldest datoids to sync with my frozenXID/minMulti values */
oldestxid_datoid = MyDatabaseId;
@@ -1120,10 +1124,10 @@ vac_truncate_clog(TransactionId frozenXID,
* as it could be.
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
+ method = relation->rd_stamroutine;
+ scan = method->scan_begin_catalog(relation, 0, NULL);
- scan = heap_beginscan_catalog(relation, 0, NULL);
-
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1160,7 +1164,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 206238a..dca2f08 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -270,25 +270,27 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
StorageTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
TupleDesc desc = RelationGetDescr(rel);
+ StorageAmRoutine *method;
Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin(rel, &snap, 0, NULL);
retry:
found = false;
- heap_rescan(scan, NULL);
+ method->scan_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scantuple = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
if (!tuple_equals_slot(desc, scantuple, searchslot))
continue;
@@ -353,7 +355,7 @@ retry:
}
}
- heap_endscan(scan);
+ method->scan_end(scan);
return found;
}
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index f8a4e7d..51d8f74 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -690,7 +690,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ node->ss.ss_currentRelation->rd_stamroutine->scan_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -745,7 +745,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -786,7 +786,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ relation->rd_stamroutine->scan_end(scanDesc);
/*
* close the heap relation.
@@ -884,9 +884,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
/*
* Even though we aren't going to do a conventional seqscan, it is useful
- * to create a HeapScanDesc --- most of the fields in it are usable.
+ * to create a StorageScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
+ scanstate->ss.ss_currentScanDesc
+ = currentRelation->rd_stamroutine->scan_begin_bm(currentRelation,
estate->es_snapshot,
0,
NULL);
@@ -1019,5 +1020,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ node->ss.ss_currentRelation->rd_stamroutine->scan_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 51fb182..a7d2ba8 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -242,7 +242,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ node->ss.ss_currentRelation->rd_stamroutine->scan_end(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -347,7 +347,7 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ scanstate->ss.ss_currentRelation->rd_stamroutine->scan_begin_sampling(scanstate->ss.ss_currentRelation,
scanstate->ss.ps.state->es_snapshot,
0, NULL,
scanstate->use_bulkread,
@@ -356,7 +356,7 @@ tablesample_init(SampleScanState *scanstate)
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ scanstate->ss.ss_currentRelation->rd_stamroutine->scan_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 9cba6b3..5096248 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -53,10 +54,12 @@ SeqNext(SeqScanState *node)
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
+ StorageAmRoutine *method;
/*
* get information from the estate and scan state
*/
+ method = node->ss.ss_currentRelation->rd_stamroutine;
scandesc = node->ss.ss_currentScanDesc;
estate = node->ss.ps.state;
direction = estate->es_direction;
@@ -68,7 +71,7 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ scandesc = method->scan_begin(node->ss.ss_currentRelation,
estate->es_snapshot,
0, NULL);
node->ss.ss_currentScanDesc = scandesc;
@@ -77,7 +80,7 @@ SeqNext(SeqScanState *node)
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
+ tuple = method->scan_getnext(scandesc, direction);
/*
* save the tuple and the buffer returned to us by the access methods in
@@ -245,7 +248,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ relation->rd_stamroutine->scan_end(scanDesc);
/*
* close the heap relation.
@@ -272,7 +275,7 @@ ExecReScanSeqScan(SeqScanState *node)
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
+ node->ss.ss_currentRelation->rd_stamroutine->scan_rescan(scan, /* scan desc */
NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 89dd3b3..e6b8ee0 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -69,6 +69,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/dependency.h"
@@ -1887,6 +1888,7 @@ get_database_list(void)
HeapScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
+ StorageAmRoutine *method;
/* This is the context that we will allocate our output data in */
resultcxt = CurrentMemoryContext;
@@ -1902,9 +1904,10 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = method->scan_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1931,7 +1934,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -1965,6 +1968,7 @@ do_autovacuum(void)
int effective_multixact_freeze_max_age;
bool did_vacuum = false;
bool found_concurrent_worker = false;
+ StorageAmRoutine *method;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
@@ -2033,6 +2037,7 @@ do_autovacuum(void)
shared = pgstat_fetch_stat_dbentry(InvalidOid);
classRel = heap_open(RelationRelationId, AccessShareLock);
+ method = classRel->rd_stamroutine;
/* create a copy so we can use it after closing pg_class */
pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
@@ -2061,13 +2066,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = method->scan_begin_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = method->scan_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2153,7 +2158,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ method->scan_end(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2161,8 +2166,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = method->scan_begin_catalog(classRel, 1, &key);
+ while ((tuple = method->scan_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2208,7 +2213,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ method->scan_end(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index f453dad..d07914b 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1209,6 +1210,7 @@ pgstat_collect_oids(Oid catalogid)
HeapScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
+ StorageAmRoutine *method;
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
@@ -1221,8 +1223,9 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin(rel, snapshot, 0, NULL);
+ while ((tup = method->scan_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1233,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ method->scan_end(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 4e2c350..9813efc 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -102,6 +102,7 @@ get_subscription_list(void)
HeapScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
+ StorageAmRoutine *method;
/* This is the context that we will allocate our output data in */
resultcxt = CurrentMemoryContext;
@@ -117,9 +118,10 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ method = rel->rd_stamroutine;
+ scan = method->scan_begin_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = method->scan_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -145,7 +147,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index fd3768d..3db218d 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -421,6 +422,7 @@ DefineQueryRewrite(char *rulename,
{
HeapScanDesc scanDesc;
Snapshot snapshot;
+ StorageAmRoutine *method;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
@@ -429,13 +431,14 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ method = event_relation->rd_stamroutine;
+ scanDesc = method->scan_begin(event_relation, snapshot, 0, NULL);
+ if (method->scan_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ method->scan_end(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index b8b4a06..400da47 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1205,13 +1206,15 @@ ThereIsAtLeastOneRole(void)
Relation pg_authid_rel;
HeapScanDesc scan;
bool result;
+ StorageAmRoutine *method;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
+ method = pg_authid_rel->rd_stamroutine;
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = method->scan_begin_catalog(pg_authid_rel, 0, NULL);
+ result = (method->scan_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ method->scan_end(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
--
2.7.4.windows.1
[application/octet-stream] 0009-Remaining-heap_insert-calls-repalce.patch (2.9K, ../../CAJrrPGfG7BarSFb6KQkOOYVaC6vjQRgG45+J2+TwLHHaFKuwGA@mail.gmail.com/12-0009-Remaining-heap_insert-calls-repalce.patch)
download | inline diff:
From 546dd54a77a190d104bc79bfdb016bc23948bd86 Mon Sep 17 00:00:00 2001
From: Hari <haribabuk@fast.au.fujitsu.com>
Date: Fri, 9 Jun 2017 11:38:36 +1000
Subject: [PATCH 09/10] Remaining heap_insert calls repalce
---
src/backend/commands/createas.c | 26 +++++++++++++-------------
src/backend/commands/matview.c | 21 ++++++++++-----------
2 files changed, 23 insertions(+), 24 deletions(-)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 06425cc..d0d167a 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,24 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
+ StorageAmRoutine *method;
+ ItemPointerData iptr;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
+ method = myState->rel->rd_stamroutine;
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
-
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ slot->tts_tupleOid = InvalidOid;
+
+ method->tuple_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ &iptr);
/* We know this is a newly created relation, so there are no indexes */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 9ffd91e..1fb94d7 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,19 +492,17 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
+ StorageAmRoutine *method;
+ ItemPointerData iptr;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecMaterializeSlot(slot);
+ method = myState->transientrel->rd_stamroutine;
- heap_insert(myState->transientrel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ method->tuple_insert(myState->transientrel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ &iptr);
/* We know this is a newly created relation, so there are no indexes */
--
2.7.4.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-06-21 19:47 ` Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 21:27 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-28 02:07 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
3 siblings, 4 replies; 138+ messages in thread
From: Robert Haas @ 2017-06-21 19:47 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Jun 12, 2017 at 9:50 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
> Open Items:
>
> 1. The BitmapHeapScan and TableSampleScan are tightly coupled with
> HeapTuple and HeapScanDesc, So these scans are directly operating
> on those structures and providing the result.
>
> These scan types may not be applicable to different storage formats.
> So how to handle them?
I think that BitmapHeapScan, at least, is applicable to any table AM
that has TIDs. It seems to me that in general we can imagine three
kinds of table AMs:
1. Table AMs where a tuple can be efficiently located by a real TID.
By a real TID, I mean that the block number part is really a block
number and the item ID is really a location within the block. These
are necessarily quite similar to our current heap, but they can change
the tuple format and page format to some degree, and it seems like in
many cases it should be possible to plug them into our existing index
AMs without too much heartache. Both index scans and bitmap index
scans ought to work.
2. Table AMs where a tuple has some other kind of locator. For
example, imagine an index-organized table where the locator is the
primary key, which is a bit like what Alvaro had in mind for indirect
indexes. If the locator is 6 bytes or less, it could potentially be
jammed into a TID, but I don't think that's a great idea. For things
like int8 or numeric, it won't work at all. Even for other things,
it's going to cause problems because the bit patterns won't be what
the code is expecting; e.g. bitmap scans care about the structure of
the TID, not just how many bits it is. (Due credit: Somebody, maybe
Alvaro, pointed out this problem before, at PGCon.) For these kinds
of tables, larger modifications to the index AMs are likely to be
necessary, at least if we want a really general solution, or maybe we
should have separate index AMs - e.g. btree for traditional TID-based
heaps, and generic_btree or indirect_btree or key_btree or whatever
for heaps with some other kind of locator. It's not too hard to see
how to make index scans work with this sort of structure but it's very
unclear to me whether, or how, bitmap scans can be made to work.
3. Table AMs where a tuple doesn't really have a locator at all. In
these cases, we can't support any sort of index AM at all. When the
table is queried, there's really nothing the core system can do except
ask the table AM for a full scan, supply the quals, and hope the table
AM has some sort of smarts that enable it to optimize somehow. For
example, you can imagine converting cstore_fdw into a table AM of this
sort - ORC has a sort of inbuilt BRIN-like indexing that allows whole
chunks to be proven uninteresting and skipped. (You could use chunk
number + offset to turn this into a table AM of the previous type if
you wanted to support secondary indexes; not sure if that'd be useful,
but it'd certainly be harder.)
I'm more interested in #1 than in #3, and more interested in #3 than
#2, but other people may have different priorities.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-06-22 01:01 ` Michael Paquier <michael.paquier@gmail.com>
2017-06-22 02:12 ` Re: Pluggable storage Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
2017-06-22 12:32 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
3 siblings, 3 replies; 138+ messages in thread
From: Michael Paquier @ 2017-06-22 01:01 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 4:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> I think that BitmapHeapScan, at least, is applicable to any table AM
> that has TIDs. It seems to me that in general we can imagine three
> kinds of table AMs:
>
> 1. Table AMs where a tuple can be efficiently located by a real TID.
> By a real TID, I mean that the block number part is really a block
> number and the item ID is really a location within the block. These
> are necessarily quite similar to our current heap, but they can change
> the tuple format and page format to some degree, and it seems like in
> many cases it should be possible to plug them into our existing index
> AMs without too much heartache. Both index scans and bitmap index
> scans ought to work.
>
> 2. Table AMs where a tuple has some other kind of locator. For
> example, imagine an index-organized table where the locator is the
> primary key, which is a bit like what Alvaro had in mind for indirect
> indexes. If the locator is 6 bytes or less, it could potentially be
> jammed into a TID, but I don't think that's a great idea. For things
> like int8 or numeric, it won't work at all. Even for other things,
> it's going to cause problems because the bit patterns won't be what
> the code is expecting; e.g. bitmap scans care about the structure of
> the TID, not just how many bits it is. (Due credit: Somebody, maybe
> Alvaro, pointed out this problem before, at PGCon.) For these kinds
> of tables, larger modifications to the index AMs are likely to be
> necessary, at least if we want a really general solution, or maybe we
> should have separate index AMs - e.g. btree for traditional TID-based
> heaps, and generic_btree or indirect_btree or key_btree or whatever
> for heaps with some other kind of locator. It's not too hard to see
> how to make index scans work with this sort of structure but it's very
> unclear to me whether, or how, bitmap scans can be made to work.
>
> 3. Table AMs where a tuple doesn't really have a locator at all. In
> these cases, we can't support any sort of index AM at all. When the
> table is queried, there's really nothing the core system can do except
> ask the table AM for a full scan, supply the quals, and hope the table
> AM has some sort of smarts that enable it to optimize somehow. For
> example, you can imagine converting cstore_fdw into a table AM of this
> sort - ORC has a sort of inbuilt BRIN-like indexing that allows whole
> chunks to be proven uninteresting and skipped. (You could use chunk
> number + offset to turn this into a table AM of the previous type if
> you wanted to support secondary indexes; not sure if that'd be useful,
> but it'd certainly be harder.)
>
> I'm more interested in #1 than in #3, and more interested in #3 than
> #2, but other people may have different priorities.
Putting that in a couple of words.
1. Table AM with a 6-byte TID.
2. Table AM with a custom locator format, which could be TID-like.
3. Table AM with no locators.
Getting into having #1 first to work out would already be really
useful for users. My take on the matter is that being able to plug in
in-core index AMs directly into a table AM #1 is more useful in the
long term, as it is possible for multiple table AMs to use the same
kind of index AM which is designed nicely enough. So the index AM
logic basically does not need to be duplicated across multiple table
AMs. #3 implies that the index AM logic is implemented in the table
AM. Not saying that it is not useful, but it does not feel natural to
have the planner request for a sequential scan, just to have the table
AM secretly do some kind of index/skipping scan.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
@ 2017-06-22 02:12 ` Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
2017-06-22 03:00 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2 siblings, 1 reply; 138+ messages in thread
From: Amit Langote @ 2017-06-22 02:12 UTC (permalink / raw)
To: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 2017/06/22 10:01, Michael Paquier wrote:
> #3 implies that the index AM logic is implemented in the table
> AM. Not saying that it is not useful, but it does not feel natural to
> have the planner request for a sequential scan, just to have the table
> AM secretly do some kind of index/skipping scan.
I had read a relevant comment on a pluggable storage thread awhile back
[1]. In short, the comment was that the planner should be able to get
some intelligence, via some API, from the heap storage implementation
about the latter's access cost characteristics. The storage should
provide accurate-enough cost information to the planner when such a
request is made by, say, cost_seqscan(), so that the planner can make
appropriate choice. If two tables containing the same number of rows (and
the same size in bytes, perhaps) use different storage implementations,
then, planner's cost parameters remaining same, cost_seqscan() will end up
calculating different costs for the two tables. Perhaps, SeqScan would be
chosen for one table but not the another based on that.
Thanks,
Amit
[1]
https://www.postgresql.org/message-id/CA%2BTgmoY3LXVUPQVdZW70XKp5PsXffO82pXXt%3DbeegcV%2B%3DRsQgg%40...
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 02:12 ` Re: Pluggable storage Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
@ 2017-06-22 03:00 ` Michael Paquier <michael.paquier@gmail.com>
0 siblings, 0 replies; 138+ messages in thread
From: Michael Paquier @ 2017-06-22 03:00 UTC (permalink / raw)
To: Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 11:12 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:
> On 2017/06/22 10:01, Michael Paquier wrote:
>> #3 implies that the index AM logic is implemented in the table
>> AM. Not saying that it is not useful, but it does not feel natural to
>> have the planner request for a sequential scan, just to have the table
>> AM secretly do some kind of index/skipping scan.
>
> I had read a relevant comment on a pluggable storage thread awhile back
> [1]. In short, the comment was that the planner should be able to get
> some intelligence, via some API, from the heap storage implementation
> about the latter's access cost characteristics. The storage should
> provide accurate-enough cost information to the planner when such a
> request is made by, say, cost_seqscan(), so that the planner can make
> appropriate choice. If two tables containing the same number of rows (and
> the same size in bytes, perhaps) use different storage implementations,
> then, planner's cost parameters remaining same, cost_seqscan() will end up
> calculating different costs for the two tables. Perhaps, SeqScan would be
> chosen for one table but not the another based on that.
Yeah, I agree that the costing part needs some clear attention and
thoughts, and the gains are absolutely huge with the correct
interface. That could be done in a later step though.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
@ 2017-06-22 12:32 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:27 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-22 12:32 UTC (permalink / raw)
To: Michael Paquier <michael.paquier@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 4:01 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:
> Putting that in a couple of words.
> 1. Table AM with a 6-byte TID.
> 2. Table AM with a custom locator format, which could be TID-like.
> 3. Table AM with no locators.
>
> Getting into having #1 first to work out would already be really
> useful for users.
What exactly would be useful for *users*? Any kind of API itself is
completely useless for users, because they are users, not developers.
Storage API could be useful for developers to implement storage AMs whose
in turn could be useful for users. Then while saying that #1 is useful for
users, it would be nice to keep in mind particular storage AMs which can be
implemented using #1.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 12:32 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-22 14:27 ` Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 23:49 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Robert Haas @ 2017-06-22 14:27 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 8:32 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> On Thu, Jun 22, 2017 at 4:01 AM, Michael Paquier <michael.paquier@gmail.com>
> wrote:
>> Putting that in a couple of words.
>> 1. Table AM with a 6-byte TID.
>> 2. Table AM with a custom locator format, which could be TID-like.
>> 3. Table AM with no locators.
>>
>> Getting into having #1 first to work out would already be really
>> useful for users.
>
> What exactly would be useful for *users*? Any kind of API itself is
> completely useless for users, because they are users, not developers.
> Storage API could be useful for developers to implement storage AMs whose in
> turn could be useful for users.
What's your point? I assume that is what Michael meant.
> Then while saying that #1 is useful for
> users, it would be nice to keep in mind particular storage AMs which can be
> implemented using #1.
I don't think anybody's arguing with that.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 12:32 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:27 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-06-22 14:36 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 21:36 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
1 sibling, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-22 14:36 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 5:27 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Jun 22, 2017 at 8:32 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > On Thu, Jun 22, 2017 at 4:01 AM, Michael Paquier <
> michael.paquier@gmail.com>
> > wrote:
> >> Putting that in a couple of words.
> >> 1. Table AM with a 6-byte TID.
> >> 2. Table AM with a custom locator format, which could be TID-like.
> >> 3. Table AM with no locators.
> >>
> >> Getting into having #1 first to work out would already be really
> >> useful for users.
> >
> > What exactly would be useful for *users*? Any kind of API itself is
> > completely useless for users, because they are users, not developers.
> > Storage API could be useful for developers to implement storage AMs
> whose in
> > turn could be useful for users.
>
> What's your point? I assume that is what Michael meant.
>
TBH, I don't understand what particular enchantments do we expect from
having #1.
This is why it's hard for me to say if #1 is good idea. It's also hard for
me to say if patch upthread is right way of implementing #1.
But, I have gut feeling that if even #1 is good idea itself, it's
definitely not what users expect from "pluggable storages".
From user side, it would be normal to expect that "pluggable storage" could
be index-organized table where index could be say LSM...
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 12:32 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:27 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-22 21:36 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
0 siblings, 0 replies; 138+ messages in thread
From: Tomas Vondra @ 2017-06-22 21:36 UTC (permalink / raw)
To: pgsql-hackers
Hi,
On 6/22/17 4:36 PM, Alexander Korotkov wrote:
> On Thu, Jun 22, 2017 at 5:27 PM, Robert Haas <robertmhaas@gmail.com
> <mailto:robertmhaas@gmail.com>> wrote:
>
> On Thu, Jun 22, 2017 at 8:32 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru <mailto:a.korotkov@postgrespro.ru>> wrote:
> > On Thu, Jun 22, 2017 at 4:01 AM, Michael Paquier <michael.paquier@gmail.com <mailto:michael.paquier@gmail.com>>
> > wrote:
> >> Putting that in a couple of words.
> >> 1. Table AM with a 6-byte TID.
> >> 2. Table AM with a custom locator format, which could be TID-like.
> >> 3. Table AM with no locators.
> >>
> >> Getting into having #1 first to work out would already be really
> >> useful for users.
> >
> > What exactly would be useful for *users*? Any kind of API itself is
> > completely useless for users, because they are users, not developers.
> > Storage API could be useful for developers to implement storage AMs whose in
> > turn could be useful for users.
>
> What's your point? I assume that is what Michael meant.
>
>
> TBH, I don't understand what particular enchantments do we expect from
> having #1.
I'd say that's one of the things we're trying to figure out in this
thread. Does it make sense to go with #1 in v1 of the patch, or do we
have to implement #2 or #3 to get some real benefit for the users?
>
> This is why it's hard for me to say if #1 is good idea. It's also hard
> for me to say if patch upthread is right way of implementing #1.
> But, I have gut feeling that if even #1 is good idea itself, it's
> definitely not what users expect from "pluggable storages".
The question is "why" do you think that. What features do you expect
from pluggable storage API that would be impossible to implement with
option #1?
I'm not trying to be annoying or anything like that - I don't know the
answer and discussing those things is exactly why this thread exists.
I do agree #1 has limitations, and that it'd be great to get API that
supports all kinds of pluggable storage implementations. But I guess
that'll take some time.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-22 12:32 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:27 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-06-22 23:49 ` Michael Paquier <michael.paquier@gmail.com>
1 sibling, 0 replies; 138+ messages in thread
From: Michael Paquier @ 2017-06-22 23:49 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 11:27 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Jun 22, 2017 at 8:32 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
>> On Thu, Jun 22, 2017 at 4:01 AM, Michael Paquier <michael.paquier@gmail.com>
>> wrote:
>>> Putting that in a couple of words.
>>> 1. Table AM with a 6-byte TID.
>>> 2. Table AM with a custom locator format, which could be TID-like.
>>> 3. Table AM with no locators.
>>>
>>> Getting into having #1 first to work out would already be really
>>> useful for users.
>>
>> What exactly would be useful for *users*? Any kind of API itself is
>> completely useless for users, because they are users, not developers.
>> Storage API could be useful for developers to implement storage AMs whose in
>> turn could be useful for users.
>
> What's your point? I assume that is what Michael meant.
Sorry for the confusion. I implied here that users are the ones
developing modules.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
@ 2017-06-23 08:38 ` Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
2 siblings, 1 reply; 138+ messages in thread
From: Teodor Sigaev @ 2017-06-23 08:38 UTC (permalink / raw)
To: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
> 1. Table AM with a 6-byte TID.
> 2. Table AM with a custom locator format, which could be TID-like.
> 3. Table AM with no locators.
Currently TID has its own type in system catalog. Seems, it's possible that
storage claims type of TID which it uses. Then, AM could claim it too, so the
core based on that information could solve the question about AM-storage
compatibility. Storage could also claim that it hasn't TID type at all so it
couldn't be used with any access method, use case: compressed column oriented
storage.
As I remeber, only GIN depends on TID format, other indexes use it as opaque
type. Except, at least, btree and GiST - they believ that internal pointers are
the same as outer (to heap)
Another dubious part - BitmapScan.
--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
@ 2017-06-23 14:07 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-23 14:24 ` Re: Pluggable storage Tom Lane <tgl@sss.pgh.pa.us>
2017-06-25 09:38 ` Re: Pluggable storage Julien Rouhaud <julien.rouhaud@dalibo.com>
0 siblings, 2 replies; 138+ messages in thread
From: Tomas Vondra @ 2017-06-23 14:07 UTC (permalink / raw)
To: Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hi,
On 6/23/17 10:38 AM, Teodor Sigaev wrote:
>> 1. Table AM with a 6-byte TID.
>> 2. Table AM with a custom locator format, which could be TID-like.
>> 3. Table AM with no locators.
>
> Currently TID has its own type in system catalog. Seems, it's possible
> that storage claims type of TID which it uses. Then, AM could claim it
> too, so the core based on that information could solve the question
> about AM-storage compatibility. Storage could also claim that it hasn't
> TID type at all so it couldn't be used with any access method, use case:
> compressed column oriented storage.
>
Isn't the fact that TID is an existing type defined in system catalogs
is a fairly insignificant detail? I mean, we could just as easily define
a new 64-bit locator data type, and use that instead, for example.
The main issue here is that we assume things about the TID contents,
i.e. that it contains page/offset etc. And Bitmap nodes rely on that, to
some extent - e.g. when prefetching data.
>
> As I remeber, only GIN depends on TID format, other indexes use it as
> opaque type. Except, at least, btree and GiST - they believe that
> internal pointers are the same as outer (to heap)
>
I think you're probably right - GIN does compress the posting lists by
exploiting the TID redundancy (that it's page/offset structure), and I
don't think there are other index AMs doing that.
But I'm not sure we can simply rely on that - it's possible people will
try to improve other index types (e.g. by adding similar compression to
other index types). Moreover we now support extensions defining custom
index AMs, and we have no clue what people may do in those.
So this would clearly require some sort of flag for each index AM.
>
> Another dubious part - BitmapScan.
>
It would be really great if you could explain why BitmapScans are
dubious, instead of just labeling them as dubious. (I guess you mean
Bitmap Heap Scans, right?)
I see no conceptual issue with bitmap scans on arbitrary locator types,
as long as there's sufficient locality information encoded in the value.
What I mean by that is that for any two locator values A and B:
(1) if (A < B) then (A is stored before B)
(2) if (A is close to B) then (A is stored close to B)
Without these features it's probably futile to try to do bitmap scans,
because the bitmap would not result in mostly sequential access pattern
and things like prefetch would not be very efficient, I think.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-06-23 14:24 ` Tom Lane <tgl@sss.pgh.pa.us>
1 sibling, 0 replies; 138+ messages in thread
From: Tom Lane @ 2017-06-23 14:24 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
> It would be really great if you could explain why BitmapScans are
> dubious, instead of just labeling them as dubious. (I guess you mean
> Bitmap Heap Scans, right?)
The two things I'm aware of are (a) the assumption you noted, that
fetching tuples in TID sort order is a reasonably efficient thing,
and (b) the "offset" part of a TID can't exceed MaxHeapTuplesPerPage
--- see data structure in tidbitmap.c. The latter issue means that
you don't really have a full six bytes to play with in a TID, only
about five.
I don't think (b) would be terribly hard to fix if we had a motivation to,
but I wonder whether there aren't other places that also know this about
TIDs.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-06-25 09:38 ` Julien Rouhaud <julien.rouhaud@dalibo.com>
2017-06-26 15:18 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Julien Rouhaud @ 2017-06-25 09:38 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 23/06/2017 16:07, Tomas Vondra wrote:
>
> I think you're probably right - GIN does compress the posting lists by
> exploiting the TID redundancy (that it's page/offset structure), and I
> don't think there are other index AMs doing that.
>
> But I'm not sure we can simply rely on that - it's possible people will
> try to improve other index types (e.g. by adding similar compression to
> other index types).
That reminds me
https://www.postgresql.org/message-id/55E4051B.7020209@postgrespro.ru
where Anastasia proposed something similar.
--
Julien Rouhaud
http://dalibo.com - http://dalibo.org
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-25 09:38 ` Re: Pluggable storage Julien Rouhaud <julien.rouhaud@dalibo.com>
@ 2017-06-26 15:18 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-26 19:55 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-26 15:18 UTC (permalink / raw)
To: Julien Rouhaud <julien.rouhaud@dalibo.com>; +Cc: Tomas Vondra <tomas.vondra@2ndquadrant.com>; Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hackers,
I see that design question for PostgreSQL pluggable storages is very hard.
BTW, I think it worth analyzing existing use-cases of pluggable storages.
I think that the most famous DBMS with pluggable storage API is MySQL.
This why I decided to start with it. I've added MySQL/MariaDB section on
wiki page.
https://wiki.postgresql.org/wiki/Future_of_storage#MySQL.2FMariaDB
It appears that significant part of MySQL storage engines are misuses.
MySQL lacks of various features like FDWs or writable views and so on.
This is why developers created a lot of pluggable storages for that
purposes. We definitely don't want something like this in PostgreSQL now.
I created special resume column where I expressed whether it would be nice
to have something like this table engine in PostgreSQL.
Any comments and additions are welcome. I'm planning to write similar
table for MongoDB.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-25 09:38 ` Re: Pluggable storage Julien Rouhaud <julien.rouhaud@dalibo.com>
2017-06-26 15:18 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-26 19:55 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-26 20:39 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Tomas Vondra @ 2017-06-26 19:55 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; Julien Rouhaud <julien.rouhaud@dalibo.com>; +Cc: Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hi,
On 06/26/2017 05:18 PM, Alexander Korotkov wrote:
> Hackers,
>
> I see that design question for PostgreSQL pluggable storages is very
> hard.
IMHO it's mostly expected to be hard.
Firstly, PostgreSQL is a mature product with many advanced features, and
reworking a low-level feature without breaking something on top of it is
hard by definition.
Secondly, project policies and code quality requirements set the bar
very high too, I think.
> BTW, I think it worth analyzing existing use-cases of pluggable
> storages. I think that the most famous DBMS with pluggable storage API
> is MySQL. This why I decided to start with it. I've added
> MySQL/MariaDB section on wiki page.
> https://wiki.postgresql.org/wiki/Future_of_storage#MySQL.2FMariaDB
> It appears that significant part of MySQL storage engines are misuses.
> MySQL lacks of various features like FDWs or writable views and so on.
> This is why developers created a lot of pluggable storages for that
> purposes. We definitely don't want something like this in PostgreSQL
> now. I created special resume column where I expressed whether it
> would be nice to have something like this table engine in PostgreSQL.
>
I don't want to discourage you, but I'm not sure how valuable this is.
I agree it's valuable to have a an over-view of use cases for pluggable
storage, but I don't think we'll get that from looking at MySQL. As you
noticed, most of the storage engines are misuses, so it's difficult to
learn anything valuable from them. You can argue that using FDWs to
implement alternative storage engines is a misuse too, but at least that
gives us a valid use case (columnar storage implemented using FDW).
If anything, the MySQL storage engines should serve as a cautionary tale
how not to do things - there's also a plenty of references in the MySQL
"Restrictions and Limitations" section of the manual:
https://downloads.mysql.com/docs/mysql-reslimits-excerpt-5.7-en.pdf
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Re: Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Re: Pluggable storage Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-25 09:38 ` Re: Pluggable storage Julien Rouhaud <julien.rouhaud@dalibo.com>
2017-06-26 15:18 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-26 19:55 ` Re: Pluggable storage Tomas Vondra <tomas.vondra@2ndquadrant.com>
@ 2017-06-26 20:39 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 0 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-26 20:39 UTC (permalink / raw)
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>; +Cc: Julien Rouhaud <julien.rouhaud@dalibo.com>; Teodor Sigaev <teodor@sigaev.ru>; Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Jun 26, 2017 at 10:55 PM, Tomas Vondra <tomas.vondra@2ndquadrant.com
> wrote:
> On 06/26/2017 05:18 PM, Alexander Korotkov wrote:
>
>> I see that design question for PostgreSQL pluggable storages is very hard.
>>
>
> IMHO it's mostly expected to be hard.
>
> Firstly, PostgreSQL is a mature product with many advanced features, and
> reworking a low-level feature without breaking something on top of it is
> hard by definition.
>
> Secondly, project policies and code quality requirements set the bar very
> high too, I think.
Sure.
> BTW, I think it worth analyzing existing use-cases of pluggable
>
>> storages. I think that the most famous DBMS with pluggable storage API
>> is MySQL. This why I decided to start with it. I've added
>> MySQL/MariaDB section on wiki page.
>> https://wiki.postgresql.org/wiki/Future_of_storage#MySQL.2FMariaDB
>> It appears that significant part of MySQL storage engines are misuses.
>> MySQL lacks of various features like FDWs or writable views and so on.
>> This is why developers created a lot of pluggable storages for that
>> purposes. We definitely don't want something like this in PostgreSQL now.
>> I created special resume column where I expressed whether it
>> would be nice to have something like this table engine in PostgreSQL.
>>
>
> I don't want to discourage you, but I'm not sure how valuable this is.
>
> I agree it's valuable to have a an over-view of use cases for pluggable
> storage, but I don't think we'll get that from looking at MySQL. As you
> noticed, most of the storage engines are misuses, so it's difficult to
> learn anything valuable from them. You can argue that using FDWs to
> implement alternative storage engines is a misuse too, but at least that
> gives us a valid use case (columnar storage implemented using FDW).
>
> If anything, the MySQL storage engines should serve as a cautionary tale
> how not to do things - there's also a plenty of references in the MySQL
> "Restrictions and Limitations" section of the manual:
>
> https://downloads.mysql.com/docs/mysql-reslimits-excerpt-5.7-en.pdf
Just to clarify the thing. I don't propose any adoption of MySQL pluggable
storage API to PostgreSQL or something like this. I just wrote this table
for completeness of vision. It may appear that somebody will make some
valuable notes out of it, it may appear that not. "Yes" in third column
means only that there is positive user visible effects which are *nice to
have* in PostgreSQL.
Also, I remember there was a table with comparison of different designs of
pluggable storages and their use-cases at PGCon 2017 unconference. Could
somebody reproduce it?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-06-22 14:30 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
3 siblings, 2 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-22 14:30 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Jun 21, 2017 at 10:47 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Mon, Jun 12, 2017 at 9:50 PM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> > Open Items:
> >
> > 1. The BitmapHeapScan and TableSampleScan are tightly coupled with
> > HeapTuple and HeapScanDesc, So these scans are directly operating
> > on those structures and providing the result.
> >
> > These scan types may not be applicable to different storage formats.
> > So how to handle them?
>
> I think that BitmapHeapScan, at least, is applicable to any table AM
> that has TIDs. It seems to me that in general we can imagine three
> kinds of table AMs:
>
> 1. Table AMs where a tuple can be efficiently located by a real TID.
> By a real TID, I mean that the block number part is really a block
> number and the item ID is really a location within the block. These
> are necessarily quite similar to our current heap, but they can change
> the tuple format and page format to some degree, and it seems like in
> many cases it should be possible to plug them into our existing index
> AMs without too much heartache. Both index scans and bitmap index
> scans ought to work.
>
If #1 is only about changing tuple and page formats, then could be much
simpler than the patch upthread? We can implement "page format access
methods" with routines for insertion, update, pruning and deletion of
tuples *in particular page*. There is no need to redefine high-level logic
for scanning heap, doing updates and so on...
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-27 13:08 ` Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-06-27 13:08 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 8:00 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> On Wed, Jun 21, 2017 at 10:47 PM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>> On Mon, Jun 12, 2017 at 9:50 PM, Haribabu Kommi
>> <kommi.haribabu@gmail.com> wrote:
>> > Open Items:
>> >
>> > 1. The BitmapHeapScan and TableSampleScan are tightly coupled with
>> > HeapTuple and HeapScanDesc, So these scans are directly operating
>> > on those structures and providing the result.
>> >
>> > These scan types may not be applicable to different storage formats.
>> > So how to handle them?
>>
>> I think that BitmapHeapScan, at least, is applicable to any table AM
>> that has TIDs. It seems to me that in general we can imagine three
>> kinds of table AMs:
>>
>> 1. Table AMs where a tuple can be efficiently located by a real TID.
>> By a real TID, I mean that the block number part is really a block
>> number and the item ID is really a location within the block. These
>> are necessarily quite similar to our current heap, but they can change
>> the tuple format and page format to some degree, and it seems like in
>> many cases it should be possible to plug them into our existing index
>> AMs without too much heartache. Both index scans and bitmap index
>> scans ought to work.
>
>
> If #1 is only about changing tuple and page formats, then could be much
> simpler than the patch upthread? We can implement "page format access
> methods" with routines for insertion, update, pruning and deletion of tuples
> *in particular page*. There is no need to redefine high-level logic for
> scanning heap, doing updates and so on...
>
If you are changing tuple format then you do need to worry about
places wherever we are using HeapTuple like TupleTableSlots,
Visibility routines, etc. (just think if somebody changes tuple
header, then all such places are susceptible to change). Similarly,
if the page format is changed you need to consider all page scan API's
like heapgettup_pagemode/heapgetpage.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-06-27 13:53 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-28 12:39 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-06-27 13:53 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Jun 27, 2017 at 4:08 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Thu, Jun 22, 2017 at 8:00 PM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > On Wed, Jun 21, 2017 at 10:47 PM, Robert Haas <robertmhaas@gmail.com>
> wrote:
> >>
> >> On Mon, Jun 12, 2017 at 9:50 PM, Haribabu Kommi
> >> <kommi.haribabu@gmail.com> wrote:
> >> > Open Items:
> >> >
> >> > 1. The BitmapHeapScan and TableSampleScan are tightly coupled with
> >> > HeapTuple and HeapScanDesc, So these scans are directly operating
> >> > on those structures and providing the result.
> >> >
> >> > These scan types may not be applicable to different storage formats.
> >> > So how to handle them?
> >>
> >> I think that BitmapHeapScan, at least, is applicable to any table AM
> >> that has TIDs. It seems to me that in general we can imagine three
> >> kinds of table AMs:
> >>
> >> 1. Table AMs where a tuple can be efficiently located by a real TID.
> >> By a real TID, I mean that the block number part is really a block
> >> number and the item ID is really a location within the block. These
> >> are necessarily quite similar to our current heap, but they can change
> >> the tuple format and page format to some degree, and it seems like in
> >> many cases it should be possible to plug them into our existing index
> >> AMs without too much heartache. Both index scans and bitmap index
> >> scans ought to work.
> >
> >
> > If #1 is only about changing tuple and page formats, then could be much
> > simpler than the patch upthread? We can implement "page format access
> > methods" with routines for insertion, update, pruning and deletion of
> tuples
> > *in particular page*. There is no need to redefine high-level logic for
> > scanning heap, doing updates and so on...
>
> If you are changing tuple format then you do need to worry about
> places wherever we are using HeapTuple like TupleTableSlots,
> Visibility routines, etc. (just think if somebody changes tuple
> header, then all such places are susceptible to change).
Agree. I think that we can consider pluggable tuple format as an
independent feature which is desirable to have before pluggable storages.
For example, I believe some FDWs could already have benefit from pluggable
tuple format.
> Similarly,
> if the page format is changed you need to consider all page scan API's
> like heapgettup_pagemode/heapgetpage.
If page format is changed, then heapgettup_pagemode/heapgetpage should use
appropriate API functions for manipulating page items. I'm very afraid of
overriding whole heapgettup_pagemode/heapgetpage and monstrous functions
like heap_update without understanding of clear use-case. It's definitely
not needed for changing heap page format.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-28 03:16 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-06-28 03:16 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Jun 27, 2017 at 11:53 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:
> On Tue, Jun 27, 2017 at 4:08 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
>
>> On Thu, Jun 22, 2017 at 8:00 PM, Alexander Korotkov
>> <a.korotkov@postgrespro.ru> wrote:
>> > On Wed, Jun 21, 2017 at 10:47 PM, Robert Haas <robertmhaas@gmail.com>
>> wrote:
>> >>
>> >> On Mon, Jun 12, 2017 at 9:50 PM, Haribabu Kommi
>> >> <kommi.haribabu@gmail.com> wrote:
>> >> > Open Items:
>> >> >
>> >> > 1. The BitmapHeapScan and TableSampleScan are tightly coupled with
>> >> > HeapTuple and HeapScanDesc, So these scans are directly operating
>> >> > on those structures and providing the result.
>> >> >
>> >> > These scan types may not be applicable to different storage formats.
>> >> > So how to handle them?
>> >>
>> >> I think that BitmapHeapScan, at least, is applicable to any table AM
>> >> that has TIDs. It seems to me that in general we can imagine three
>> >> kinds of table AMs:
>> >>
>> >> 1. Table AMs where a tuple can be efficiently located by a real TID.
>> >> By a real TID, I mean that the block number part is really a block
>> >> number and the item ID is really a location within the block. These
>> >> are necessarily quite similar to our current heap, but they can change
>> >> the tuple format and page format to some degree, and it seems like in
>> >> many cases it should be possible to plug them into our existing index
>> >> AMs without too much heartache. Both index scans and bitmap index
>> >> scans ought to work.
>> >
>> >
>> > If #1 is only about changing tuple and page formats, then could be much
>> > simpler than the patch upthread? We can implement "page format access
>> > methods" with routines for insertion, update, pruning and deletion of
>> tuples
>> > *in particular page*. There is no need to redefine high-level logic for
>> > scanning heap, doing updates and so on...
>>
>> If you are changing tuple format then you do need to worry about
>> places wherever we are using HeapTuple like TupleTableSlots,
>> Visibility routines, etc. (just think if somebody changes tuple
>> header, then all such places are susceptible to change).
>
>
> Agree. I think that we can consider pluggable tuple format as an
> independent feature which is desirable to have before pluggable storages.
> For example, I believe some FDWs could already have benefit from pluggable
> tuple format.
>
Accepting multiple tuple format is possible with complete replacement of
HeapTuple with TupleTableSlot or something like value/null array
instead of a single memory chunk tuple data.
Currently I am working on it to replace the HeapTuple with TupleTableSlot
in the upper layers once the tuples is returned from the scan. In most of
the
places where the HeapTuple is present, either replace it with TupleTableSlot
or change it to StorageTuple (void *).
I am yet to evaluate whether it is possible to support as an independent
feature
without the need of some heap format function to understand the tuple format
in every place.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-07-14 13:35 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-07-14 13:35 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Jun 28, 2017 at 1:16 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> Accepting multiple tuple format is possible with complete replacement of
> HeapTuple with TupleTableSlot or something like value/null array
> instead of a single memory chunk tuple data.
>
> Currently I am working on it to replace the HeapTuple with TupleTableSlot
> in the upper layers once the tuples is returned from the scan. In most of
> the
> places where the HeapTuple is present, either replace it with
> TupleTableSlot
> or change it to StorageTuple (void *).
>
> I am yet to evaluate whether it is possible to support as an independent
> feature
> without the need of some heap format function to understand the tuple
> format
> in every place.
>
To replace tuple with slot, I took trigger and SPI calls as the first step
in modifying
from tuple to slot, Here I attached a WIP patch. The notable changes are,
1. Replace most of the HeapTuple with Slot in SPI interface functions.
2. In SPITupleTable, Instead of HeapTuple, it is changed to TupleTableSlot.
But this change may not be a proper approach, because a duplicate copy of
TupleTableSlot is generated and stored.
3. Changed all trigger interfaces to accept TupleTableSlot Instead of
HeapTuple.
4. ItemPointerData is added as a member to the TupleTableSlot structure.
5. Modified the ExecInsert and others to work directly on TupleTableSlot
instead
of tuple(not completely).
In many places while accessing system tables, the tuple is directly mapped
to
a catalog table structure and accessed. Currently I am not modifying any of
those,
and leaving them as it is until we solve all other HeapTuple replacement
problems.
In order to continue further changes in replacing the HeapTuple with
TupleTableSlot,
I am just thinking of replacing Trigger functions like plpgsql_exec_trigger
to return
TupleTableSlot instead of HeapTuple. I am thinking that these changes may
improve
the performance as it avoids the deform and forming a tuple. I am thinking
that
same TupleTableSlot memory is valid across these function calls. Am I
correct?
I am thinking that it may not possible to replace all the places of
HeapTuple with
TupleTableSlot, but it is possible with a StorageTuple (which is a void*).
wherever
the tuple is present, there exists a tupledesc in most of the cases. How
about
adding some kind of information in tupledesc to find out the tuple format
and call
the necessary functions to generate a TupleTableSlot from it and use that
from there
onward? This approach may be useful for Storing StorageTuple in
SPITupleTable
instead of TupleTableSlot.
Please let me know your comments/suggestions.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0001-WIP-tuple-replace-with-slot.patch (152.0K, ../../CAJrrPGc=bJnB78CfumpM_uNz+Sf3D4hp2tQHWw_T40jQEgv8LQ@mail.gmail.com/3-0001-WIP-tuple-replace-with-slot.patch)
download | inline diff:
From b98a8b002a0fddbbd665a55abcc820273a88b0f3 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommih@localhost.localdomain>
Date: Fri, 14 Jul 2017 22:13:09 +1000
Subject: [PATCH] WIP tuple replace with slot
---
contrib/dblink/dblink.c | 38 ++--
contrib/lo/lo.c | 24 +--
contrib/spi/autoinc.c | 16 +-
contrib/spi/insert_username.c | 10 +-
contrib/spi/moddatetime.c | 9 +-
contrib/spi/refint.c | 40 ++--
contrib/spi/timetravel.c | 40 ++--
contrib/tablefunc/tablefunc.c | 34 ++--
contrib/tcn/tcn.c | 6 +-
contrib/xml2/xpath.c | 8 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 12 +-
src/backend/catalog/index.c | 4 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/constraint.c | 14 +-
src/backend/commands/copy.c | 27 ++-
src/backend/commands/dbcommands.c | 1 +
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/tablecmds.c | 14 +-
src/backend/commands/trigger.c | 334 +++++++++++++++++--------------
src/backend/executor/execReplication.c | 7 +-
src/backend/executor/execTuples.c | 60 ++++++
src/backend/executor/nodeModifyTable.c | 57 ++++--
src/backend/executor/spi.c | 92 +++++++--
src/backend/replication/logical/worker.c | 1 +
src/backend/utils/adt/ri_triggers.c | 230 ++++++++++-----------
src/backend/utils/adt/ruleutils.c | 50 +++--
src/backend/utils/adt/trigfuncs.c | 4 +-
src/backend/utils/adt/tsquery_rewrite.c | 4 +-
src/backend/utils/adt/tsvector_op.c | 26 ++-
src/backend/utils/adt/xml.c | 2 -
src/include/access/htup.h | 1 +
src/include/commands/trigger.h | 26 ++-
src/include/executor/spi.h | 8 +-
src/include/executor/tuptable.h | 6 +
src/pl/plperl/plperl.c | 20 +-
src/pl/plpgsql/src/pl_comp.c | 2 +-
src/pl/plpgsql/src/pl_exec.c | 102 ++++------
src/pl/plpgsql/src/plpgsql.h | 2 +-
src/pl/plpython/plpy_exec.c | 14 +-
src/pl/tcl/pltcl.c | 10 +-
src/test/modules/worker_spi/worker_spi.c | 2 -
src/test/regress/regress.c | 55 +++--
47 files changed, 779 insertions(+), 649 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 81136b1..cad2039 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -107,7 +107,7 @@ static char *get_sql_delete(Relation rel, int *pkattnums, int pknumatts, char **
static char *get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals);
static char *quote_ident_cstr(char *rawstr);
static int get_attnum_pk_pos(int *pkattnums, int pknumatts, int key);
-static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
+static TupleTableSlot* get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals);
static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclMode aclmode);
static char *generate_relation_name(Relation rel);
static void dblink_connstr_check(const char *connstr);
@@ -2144,7 +2144,7 @@ static char *
get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
{
char *relname;
- HeapTuple tuple;
+ TupleTableSlot *slot;
TupleDesc tupdesc;
int natts;
StringInfoData buf;
@@ -2158,15 +2158,15 @@ get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
/* get relation name including any needed schema prefix and quoting */
relname = generate_relation_name(rel);
- tupdesc = rel->rd_att;
- natts = tupdesc->natts;
-
- tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
- if (!tuple)
+ slot = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
+ if (!slot)
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("source row not found")));
+ tupdesc = slot->tts_tupleDescriptor;
+ natts = tupdesc->natts;
+
appendStringInfo(&buf, "INSERT INTO %s(", relname);
needComma = false;
@@ -2202,7 +2202,7 @@ get_sql_insert(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
if (key >= 0)
val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
else
- val = SPI_getvalue(tuple, tupdesc, i + 1);
+ val = SPI_getvalue(slot, i + 1);
if (val != NULL)
{
@@ -2258,7 +2258,7 @@ static char *
get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals, char **tgt_pkattvals)
{
char *relname;
- HeapTuple tuple;
+ TupleTableSlot *slot;
TupleDesc tupdesc;
int natts;
StringInfoData buf;
@@ -2272,15 +2272,15 @@ get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
/* get relation name including any needed schema prefix and quoting */
relname = generate_relation_name(rel);
- tupdesc = rel->rd_att;
- natts = tupdesc->natts;
-
- tuple = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
- if (!tuple)
+ slot = get_tuple_of_interest(rel, pkattnums, pknumatts, src_pkattvals);
+ if (!slot)
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("source row not found")));
+ tupdesc = slot->tts_tupleDescriptor;
+ natts = tupdesc->natts;
+
appendStringInfo(&buf, "UPDATE %s SET ", relname);
/*
@@ -2303,7 +2303,7 @@ get_sql_update(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals
if (key >= 0)
val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL;
else
- val = SPI_getvalue(tuple, tupdesc, i + 1);
+ val = SPI_getvalue(slot, i + 1);
if (val != NULL)
{
@@ -2372,7 +2372,7 @@ get_attnum_pk_pos(int *pkattnums, int pknumatts, int key)
return -1;
}
-static HeapTuple
+static TupleTableSlot *
get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pkattvals)
{
char *relname;
@@ -2380,7 +2380,7 @@ get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pk
int natts;
StringInfoData buf;
int ret;
- HeapTuple tuple;
+ TupleTableSlot *slot;
int i;
/*
@@ -2456,10 +2456,10 @@ get_tuple_of_interest(Relation rel, int *pkattnums, int pknumatts, char **src_pk
{
SPITupleTable *tuptable = SPI_tuptable;
- tuple = SPI_copytuple(tuptable->vals[0]);
+ slot = tuptable->vals[0];
SPI_finish();
- return tuple;
+ return slot;
}
else
{
diff --git a/contrib/lo/lo.c b/contrib/lo/lo.c
index 4585923..432bedb 100644
--- a/contrib/lo/lo.c
+++ b/contrib/lo/lo.c
@@ -27,10 +27,10 @@ lo_manage(PG_FUNCTION_ARGS)
int attnum; /* attribute number to monitor */
char **args; /* Args containing attr name */
TupleDesc tupdesc; /* Tuple Descriptor */
- HeapTuple rettuple; /* Tuple to be returned */
+ TupleTableSlot* retslot; /* Tuple to be returned */
bool isdelete; /* are we deleting? */
- HeapTuple newtuple; /* The new value for tuple */
- HeapTuple trigtuple; /* The original value of tuple */
+ TupleTableSlot* newslot; /* The new value for tuple */
+ TupleTableSlot* trigslot; /* The original value of tuple */
if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
elog(ERROR, "%s: not fired by trigger manager",
@@ -43,8 +43,8 @@ lo_manage(PG_FUNCTION_ARGS)
/*
* Fetch some values from trigdata
*/
- newtuple = trigdata->tg_newtuple;
- trigtuple = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_newslot;
+ trigslot = trigdata->tg_trigslot;
tupdesc = trigdata->tg_relation->rd_att;
args = trigdata->tg_trigger->tgargs;
@@ -54,9 +54,9 @@ lo_manage(PG_FUNCTION_ARGS)
/* tuple to return to Executor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = newtuple;
+ retslot = newslot;
else
- rettuple = trigtuple;
+ retslot = trigslot;
/* Are we deleting the row? */
isdelete = TRIGGER_FIRED_BY_DELETE(trigdata->tg_event);
@@ -74,10 +74,10 @@ lo_manage(PG_FUNCTION_ARGS)
* Here, if the value of the monitored attribute changes, then the large
* object associated with the original value is unlinked.
*/
- if (newtuple != NULL)
+ if (newslot != NULL)
{
- char *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
- char *newv = SPI_getvalue(newtuple, tupdesc, attnum);
+ char *orig = SPI_getvalue(trigslot, attnum);
+ char *newv = SPI_getvalue(newslot, attnum);
if (orig != NULL && (newv == NULL || strcmp(orig, newv) != 0))
DirectFunctionCall1(be_lo_unlink,
@@ -96,7 +96,7 @@ lo_manage(PG_FUNCTION_ARGS)
*/
if (isdelete)
{
- char *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
+ char *orig = SPI_getvalue(trigslot, attnum);
if (orig != NULL)
{
@@ -107,5 +107,5 @@ lo_manage(PG_FUNCTION_ARGS)
}
}
- return PointerGetDatum(rettuple);
+ return PointerGetDatum(retslot->tts_storageam->slot_get_tuple(retslot));
}
diff --git a/contrib/spi/autoinc.c b/contrib/spi/autoinc.c
index 8bf7422..b4a207e 100644
--- a/contrib/spi/autoinc.c
+++ b/contrib/spi/autoinc.c
@@ -28,7 +28,7 @@ autoinc(PG_FUNCTION_ARGS)
char **args; /* arguments */
char *relname; /* triggered relation name */
Relation rel; /* triggered relation */
- HeapTuple rettuple = NULL;
+ TupleTableSlot *retslot = NULL;
TupleDesc tupdesc; /* tuple description */
bool isnull;
int i;
@@ -44,9 +44,9 @@ autoinc(PG_FUNCTION_ARGS)
elog(ERROR, "must be fired before event");
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- rettuple = trigdata->tg_trigtuple;
+ retslot = trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = trigdata->tg_newtuple;
+ retslot = trigdata->tg_newslot;
else
/* internal error */
elog(ERROR, "cannot process DELETE events");
@@ -86,7 +86,7 @@ autoinc(PG_FUNCTION_ARGS)
errmsg("attribute \"%s\" of \"%s\" must be type INT4",
args[i], relname)));
- val = DatumGetInt32(SPI_getbinval(rettuple, tupdesc, attnum, &isnull));
+ val = DatumGetInt32(SPI_getbinval(retslot, attnum, &isnull));
if (!isnull && val != 0)
{
@@ -113,9 +113,9 @@ autoinc(PG_FUNCTION_ARGS)
if (chnattrs > 0)
{
- rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
- chnattrs, chattrs,
- newvals, newnulls);
+ retslot = heap_modify_slot_by_cols(retslot,
+ chnattrs, chattrs,
+ newvals, newnulls);
}
pfree(relname);
@@ -123,5 +123,5 @@ autoinc(PG_FUNCTION_ARGS)
pfree(newvals);
pfree(newnulls);
- return PointerGetDatum(rettuple);
+ return PointerGetDatum(retslot->tts_tuple);
}
diff --git a/contrib/spi/insert_username.c b/contrib/spi/insert_username.c
index a2e1747..f3093ed 100644
--- a/contrib/spi/insert_username.c
+++ b/contrib/spi/insert_username.c
@@ -29,7 +29,7 @@ insert_username(PG_FUNCTION_ARGS)
char **args; /* arguments */
char *relname; /* triggered relation name */
Relation rel; /* triggered relation */
- HeapTuple rettuple = NULL;
+ TupleTableSlot *retslot;
TupleDesc tupdesc; /* tuple description */
int attnum;
@@ -45,9 +45,9 @@ insert_username(PG_FUNCTION_ARGS)
elog(ERROR, "insert_username: must be fired before event");
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- rettuple = trigdata->tg_trigtuple;
+ retslot = trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = trigdata->tg_newtuple;
+ retslot = trigdata->tg_newslot;
else
/* internal error */
elog(ERROR, "insert_username: cannot process DELETE events");
@@ -83,10 +83,10 @@ insert_username(PG_FUNCTION_ARGS)
newnull = false;
/* construct new tuple */
- rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
+ retslot = heap_modify_slot_by_cols(retslot,
1, &attnum, &newval, &newnull);
pfree(relname);
- return PointerGetDatum(rettuple);
+ return PointerGetDatum(retslot->tts_tuple);
}
diff --git a/contrib/spi/moddatetime.c b/contrib/spi/moddatetime.c
index 70476f7..2ab74a7 100644
--- a/contrib/spi/moddatetime.c
+++ b/contrib/spi/moddatetime.c
@@ -39,7 +39,7 @@ moddatetime(PG_FUNCTION_ARGS)
char **args; /* arguments */
char *relname; /* triggered relation name */
Relation rel; /* triggered relation */
- HeapTuple rettuple = NULL;
+ TupleTableSlot *slot;
TupleDesc tupdesc; /* tuple description */
if (!CALLED_AS_TRIGGER(fcinfo))
@@ -58,7 +58,7 @@ moddatetime(PG_FUNCTION_ARGS)
/* internal error */
elog(ERROR, "moddatetime: cannot process INSERT events");
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = trigdata->tg_newtuple;
+ slot = trigdata->tg_newslot;
else
/* internal error */
elog(ERROR, "moddatetime: cannot process DELETE events");
@@ -120,11 +120,10 @@ moddatetime(PG_FUNCTION_ARGS)
newdtnull = false;
/* Replace the attnum'th column with newdt */
- rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
- 1, &attnum, &newdt, &newdtnull);
+ slot = heap_modify_slot_by_cols(slot, 1, &attnum, &newdt, &newdtnull);
/* Clean up */
pfree(relname);
- return PointerGetDatum(rettuple);
+ return PointerGetDatum(slot->tts_tuple);
}
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
index 46205c7..0d87f59 100644
--- a/contrib/spi/refint.c
+++ b/contrib/spi/refint.c
@@ -53,7 +53,7 @@ check_primary_key(PG_FUNCTION_ARGS)
Datum *kvals; /* key values */
char *relname; /* referenced relation name */
Relation rel; /* triggered relation */
- HeapTuple tuple = NULL; /* tuple to return */
+ TupleTableSlot *slot = NULL; /* tuple to return */
TupleDesc tupdesc; /* tuple description */
EPlan *plan; /* prepared plan */
Oid *argtypes = NULL; /* key types to prepare execution plan */
@@ -82,7 +82,7 @@ check_primary_key(PG_FUNCTION_ARGS)
/* If INSERTion then must check Tuple to being inserted */
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- tuple = trigdata->tg_trigtuple;
+ slot = trigdata->tg_trigslot;
/* Not should be called for DELETE */
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
@@ -91,7 +91,7 @@ check_primary_key(PG_FUNCTION_ARGS)
/* If UPDATE, then must check new Tuple, not old one */
else
- tuple = trigdata->tg_newtuple;
+ slot = trigdata->tg_newslot;
trigger = trigdata->tg_trigger;
nargs = trigger->tgnargs;
@@ -142,7 +142,7 @@ check_primary_key(PG_FUNCTION_ARGS)
args[i], SPI_getrelname(rel))));
/* Well, get binary (in internal format) value of column */
- kvals[i] = SPI_getbinval(tuple, tupdesc, fnumber, &isnull);
+ kvals[i] = SPI_getbinval(slot, fnumber, &isnull);
/*
* If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
@@ -152,7 +152,7 @@ check_primary_key(PG_FUNCTION_ARGS)
if (isnull)
{
SPI_finish();
- return PointerGetDatum(tuple);
+ return PointerGetDatum(slot->tts_tuple);
}
if (plan->nplans <= 0) /* Get typeId of column */
@@ -217,7 +217,7 @@ check_primary_key(PG_FUNCTION_ARGS)
SPI_finish();
- return PointerGetDatum(tuple);
+ return PointerGetDatum(slot->tts_tuple);
}
/*
@@ -248,8 +248,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
Datum *kvals; /* key values */
char *relname; /* referencing relation name */
Relation rel; /* triggered relation */
- HeapTuple trigtuple = NULL; /* tuple to being changed */
- HeapTuple newtuple = NULL; /* tuple to return */
+ TupleTableSlot* trigslot = NULL; /* tuple to being changed */
+ TupleTableSlot* newslot = NULL; /* tuple to return */
TupleDesc tupdesc; /* tuple description */
EPlan *plan; /* prepared plan(s) */
Oid *argtypes = NULL; /* key types to prepare execution plan */
@@ -285,7 +285,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
elog(ERROR, "check_foreign_key: cannot process INSERT events");
/* Have to check tg_trigtuple - tuple being deleted */
- trigtuple = trigdata->tg_trigtuple;
+ trigslot = trigdata->tg_trigslot;
/*
* But if this is UPDATE then we have to return tg_newtuple. Also, if key
@@ -294,7 +294,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
is_update = 0;
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
- newtuple = trigdata->tg_newtuple;
+ newslot = trigdata->tg_newslot;
is_update = 1;
}
trigger = trigdata->tg_trigger;
@@ -369,7 +369,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
args[i], SPI_getrelname(rel))));
/* Well, get binary (in internal format) value of column */
- kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull);
+ kvals[i] = SPI_getbinval(trigslot, fnumber, &isnull);
/*
* If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()!
@@ -379,7 +379,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
if (isnull)
{
SPI_finish();
- return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
+ return PointerGetDatum((newslot == NULL) ?
+ trigslot->tts_tuple : newslot->tts_tuple);
}
/*
@@ -387,16 +388,16 @@ check_foreign_key(PG_FUNCTION_ARGS)
* compare is this the same as old one. For the moment we use string
* presentation of values...
*/
- if (newtuple != NULL)
+ if (newslot != NULL)
{
- char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber);
+ char *oldval = SPI_getvalue(trigslot, fnumber);
char *newval;
/* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */
if (oldval == NULL)
/* internal error */
elog(ERROR, "check_foreign_key: SPI_getvalue returned %d", SPI_result);
- newval = SPI_getvalue(newtuple, tupdesc, fnumber);
+ newval = SPI_getvalue(newslot, fnumber);
if (newval == NULL || strcmp(oldval, newval) != 0)
isequal = false;
}
@@ -470,7 +471,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
Assert(fn > 0); /* already checked above */
- nv = SPI_getvalue(newtuple, tupdesc, fn);
+ nv = SPI_getvalue(newslot, fn);
type = SPI_gettype(tupdesc, fn);
if ((strcmp(type, "text") && strcmp(type, "varchar") &&
@@ -552,10 +553,10 @@ check_foreign_key(PG_FUNCTION_ARGS)
/*
* If UPDATE and key is not changed ...
*/
- if (newtuple != NULL && isequal)
+ if (newslot != NULL && isequal)
{
SPI_finish();
- return PointerGetDatum(newtuple);
+ return PointerGetDatum(newslot->tts_tuple);
}
/*
@@ -604,7 +605,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
SPI_finish();
- return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple);
+ return PointerGetDatum((newslot == NULL) ?
+ trigslot->tts_tuple : newslot->tts_tuple);
}
static EPlan *
diff --git a/contrib/spi/timetravel.c b/contrib/spi/timetravel.c
index f7905e2..2127cd6 100644
--- a/contrib/spi/timetravel.c
+++ b/contrib/spi/timetravel.c
@@ -96,8 +96,8 @@ timetravel(PG_FUNCTION_ARGS)
char *cnulls; /* column nulls */
char *relname; /* triggered relation name */
Relation rel; /* triggered relation */
- HeapTuple trigtuple;
- HeapTuple newtuple = NULL;
+ TupleTableSlot *trigslot;
+ TupleTableSlot *newslot = NULL;
HeapTuple rettuple;
TupleDesc tupdesc; /* tuple description */
int natts; /* # of attributes */
@@ -129,9 +129,9 @@ timetravel(PG_FUNCTION_ARGS)
isinsert = true;
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- newtuple = trigdata->tg_newtuple;
+ newslot = trigdata->tg_newslot;
- trigtuple = trigdata->tg_trigtuple;
+ trigslot = trigdata->tg_trigslot;
rel = trigdata->tg_relation;
relname = SPI_getrelname(rel);
@@ -141,7 +141,8 @@ timetravel(PG_FUNCTION_ARGS)
{
/* OFF - nothing to do */
pfree(relname);
- return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
+ return PointerGetDatum((newslot != NULL) ?
+ newslot->tts_tuple : trigslot->tts_tuple);
}
trigger = trigdata->tg_trigger;
@@ -186,7 +187,7 @@ timetravel(PG_FUNCTION_ARGS)
Datum newvals[MaxAttrNum];
bool newnulls[MaxAttrNum];
- oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
+ oldtimeon = SPI_getbinval(trigslot, attnum[a_time_on], &isnull);
if (isnull)
{
newvals[chnattrs] = GetCurrentAbsoluteTime();
@@ -195,7 +196,7 @@ timetravel(PG_FUNCTION_ARGS)
chnattrs++;
}
- oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
+ oldtimeoff = SPI_getbinval(trigslot, attnum[a_time_off], &isnull);
if (isnull)
{
if ((chnattrs == 0 && DatumGetInt32(oldtimeon) >= NOEND_ABSTIME) ||
@@ -215,7 +216,7 @@ timetravel(PG_FUNCTION_ARGS)
pfree(relname);
if (chnattrs <= 0)
- return PointerGetDatum(trigtuple);
+ return PointerGetDatum(trigslot->tts_tuple);
if (argc == MaxAttrNum)
{
@@ -235,7 +236,8 @@ timetravel(PG_FUNCTION_ARGS)
chattrs[chnattrs] = attnum[a_ins_user];
chnattrs++;
}
- rettuple = heap_modify_tuple_by_cols(trigtuple, tupdesc,
+ rettuple = heap_modify_tuple_by_cols(trigslot->tts_tuple,
+ tupdesc,
chnattrs, chattrs,
newvals, newnulls);
return PointerGetDatum(rettuple);
@@ -243,11 +245,11 @@ timetravel(PG_FUNCTION_ARGS)
}
/* UPDATE/DELETE: */
- oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
+ oldtimeon = SPI_getbinval(trigslot, attnum[a_time_on], &isnull);
if (isnull)
elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
- oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
+ oldtimeoff = SPI_getbinval(trigslot, attnum[a_time_off], &isnull);
if (isnull)
elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
@@ -255,13 +257,13 @@ timetravel(PG_FUNCTION_ARGS)
* If DELETE/UPDATE of tuple with stop_date neq INFINITY then say upper
* Executor to skip operation for this tuple
*/
- if (newtuple != NULL)
+ if (newslot != NULL)
{ /* UPDATE */
- newtimeon = SPI_getbinval(newtuple, tupdesc, attnum[a_time_on], &isnull);
+ newtimeon = SPI_getbinval(newslot, attnum[a_time_on], &isnull);
if (isnull)
elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
- newtimeoff = SPI_getbinval(newtuple, tupdesc, attnum[a_time_off], &isnull);
+ newtimeoff = SPI_getbinval(newslot, attnum[a_time_off], &isnull);
if (isnull)
elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
@@ -286,7 +288,7 @@ timetravel(PG_FUNCTION_ARGS)
cnulls = (char *) palloc(natts * sizeof(char));
for (i = 0; i < natts; i++)
{
- cvals[i] = SPI_getbinval(trigtuple, tupdesc, i + 1, &isnull);
+ cvals[i] = SPI_getbinval(trigslot, i + 1, &isnull);
cnulls[i] = (isnull) ? 'n' : ' ';
}
@@ -294,7 +296,7 @@ timetravel(PG_FUNCTION_ARGS)
cvals[attnum[a_time_off] - 1] = newtimeoff; /* stop_date eq current date */
cnulls[attnum[a_time_off] - 1] = ' ';
- if (!newtuple)
+ if (!newslot)
{ /* DELETE */
if (argc == MaxAttrNum)
{
@@ -362,7 +364,7 @@ timetravel(PG_FUNCTION_ARGS)
elog(ERROR, "timetravel (%s): SPI_execp returned %d", relname, ret);
/* Tuple to return to upper Executor ... */
- if (newtuple)
+ if (newslot)
{ /* UPDATE */
int chnattrs = 0;
int chattrs[MaxAttrNum];
@@ -402,11 +404,11 @@ timetravel(PG_FUNCTION_ARGS)
* Use SPI_modifytuple() here because we are inside SPI environment
* but rettuple must be allocated in caller's context.
*/
- rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls);
+ rettuple = SPI_modifytuple(rel, newslot->tts_tuple, chnattrs, chattrs, newvals, newnulls);
}
else
/* DELETE case */
- rettuple = trigtuple;
+ rettuple = trigslot->tts_tuple;
SPI_finish(); /* don't forget say Bye to SPI mgr */
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 0bc8177..cd11947 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -492,7 +492,7 @@ crosstab(PG_FUNCTION_ARGS)
*/
for (i = 0; i < num_categories; i++)
{
- HeapTuple spi_tuple;
+ TupleTableSlot* spislot;
char *rowid;
/* see if we've gone too far already */
@@ -500,10 +500,10 @@ crosstab(PG_FUNCTION_ARGS)
break;
/* get the next sql result tuple */
- spi_tuple = spi_tuptable->vals[call_cntr];
+ spislot = spi_tuptable->vals[call_cntr];
/* get the rowid from the current sql result tuple */
- rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+ rowid = SPI_getvalue(spislot, 1);
/*
* If this is the first pass through the values for this rowid,
@@ -538,7 +538,7 @@ crosstab(PG_FUNCTION_ARGS)
* Be careful to assign the value to the array index based on
* which category we are presently processing.
*/
- values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);
+ values[1 + i] = SPI_getvalue(spislot, 3);
/*
* increment the counter since we consume a row for each
@@ -756,13 +756,13 @@ load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
{
crosstab_cat_desc *catdesc;
char *catname;
- HeapTuple spi_tuple;
+ TupleTableSlot* spislot;
/* get the next sql result tuple */
- spi_tuple = spi_tuptable->vals[i];
+ spislot = spi_tuptable->vals[i];
/* get the category from the current sql result tuple */
- catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+ catname = SPI_getvalue(spislot, 1);
SPIcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -875,15 +875,15 @@ get_crosstab_tuplestore(char *sql,
for (i = 0; i < proc; i++)
{
- HeapTuple spi_tuple;
+ TupleTableSlot* spislot;
crosstab_cat_desc *catdesc;
char *catname;
/* get the next sql result tuple */
- spi_tuple = spi_tuptable->vals[i];
+ spislot = spi_tuptable->vals[i];
/* get the rowid from the current sql result tuple */
- rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+ rowid = SPI_getvalue(spislot, 1);
/*
* if we're on a new output row, grab the column values up to
@@ -908,14 +908,14 @@ get_crosstab_tuplestore(char *sql,
values[0] = rowid;
for (j = 1; j < ncols - 2; j++)
- values[j] = SPI_getvalue(spi_tuple, spi_tupdesc, j + 1);
+ values[j] = SPI_getvalue(spislot, j + 1);
/* we're no longer on the first pass */
firstpass = false;
}
/* look up the category and fill in the appropriate column */
- catname = SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1);
+ catname = SPI_getvalue(spislot, ncols - 1);
if (catname != NULL)
{
@@ -923,7 +923,7 @@ get_crosstab_tuplestore(char *sql,
if (catdesc)
values[catdesc->attidx + ncols - 2] =
- SPI_getvalue(spi_tuple, spi_tupdesc, ncols);
+ SPI_getvalue(spislot, ncols);
}
xpfree(lastrowid);
@@ -1310,7 +1310,7 @@ build_tuplestore_recursively(char *key_fld,
/* Check for qualifying tuples */
if ((ret == SPI_OK_SELECT) && (proc > 0))
{
- HeapTuple spi_tuple;
+ TupleTableSlot* spislot;
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc spi_tupdesc = tuptable->tupdesc;
uint64 i;
@@ -1335,13 +1335,13 @@ build_tuplestore_recursively(char *key_fld,
appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim);
/* get the next sql result tuple */
- spi_tuple = tuptable->vals[i];
+ spislot = tuptable->vals[i];
/* get the current key (might be NULL) */
- current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
+ current_key = SPI_getvalue(spislot, 1);
/* get the parent key (might be NULL) */
- current_key_parent = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
+ current_key_parent = SPI_getvalue(spislot, 2);
/* get the current level */
sprintf(current_level, "%d", level);
diff --git a/contrib/tcn/tcn.c b/contrib/tcn/tcn.c
index 0b9acbf..c311a3b 100644
--- a/contrib/tcn/tcn.c
+++ b/contrib/tcn/tcn.c
@@ -58,7 +58,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
TriggerData *trigdata = (TriggerData *) fcinfo->context;
Trigger *trigger;
int nargs;
- HeapTuple trigtuple;
+ TupleTableSlot* trigslot;
Relation rel;
TupleDesc tupdesc;
char *channel;
@@ -112,7 +112,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
channel = trigger->tgargs[0];
/* get tuple data */
- trigtuple = trigdata->tg_trigtuple;
+ trigslot = trigdata->tg_trigslot;
rel = trigdata->tg_relation;
tupdesc = rel->rd_att;
@@ -157,7 +157,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
appendStringInfoCharMacro(payload, ',');
strcpy_quoted(payload, NameStr((tupdesc->attrs[colno - 1])->attname), '"');
appendStringInfoCharMacro(payload, '=');
- strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
+ strcpy_quoted(payload, SPI_getvalue(trigslot, colno), '\'');
}
Async_Notify(channel, payload->data);
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index 95e580d..b344d1f 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -533,7 +533,7 @@ xpath_table(PG_FUNCTION_ARGS)
/* SPI (input tuple) support */
SPITupleTable *tuptable;
- HeapTuple spi_tuple;
+ TupleTableSlot* spislot;
TupleDesc spi_tupdesc;
/* Output tuple (tuplestore) support */
@@ -702,9 +702,9 @@ xpath_table(PG_FUNCTION_ARGS)
xmlXPathCompExprPtr comppath;
/* Extract the row data as C Strings */
- spi_tuple = tuptable->vals[i];
- pkey = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
- xmldoc = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
+ spislot = tuptable->vals[i];
+ pkey = SPI_getvalue(spislot, 1);
+ xmldoc = SPI_getvalue(spislot, 2);
/*
* Clear the values array, so that not-well-formed documents
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b3f0b3c..d09a0fa 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -564,7 +564,7 @@ boot_openrel(char *relname)
struct typmap **app;
Relation rel;
HeapScanDesc scan;
- HeapTuple tup;
+ HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
relname[NAMEDATALEN - 1] = '\0';
@@ -880,7 +880,7 @@ gettype(char *type)
int i;
Relation rel;
HeapScanDesc scan;
- HeapTuple tup;
+ HeapTuple tup;
struct typmap **app;
if (Typ != NULL)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a..38a1d06 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ScanKeyData key[1];
Relation rel;
HeapScanDesc scan;
- HeapTuple tuple;
+ HeapTuple tup;
ScanKeyInit(&key[0],
Anum_pg_proc_pronamespace,
@@ -799,9 +799,9 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
rel = heap_open(ProcedureRelationId, AccessShareLock);
scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- objects = lappend_oid(objects, HeapTupleGetOid(tuple));
+ objects = lappend_oid(objects, HeapTupleGetOid(tup));
}
heap_endscan(scan);
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
ScanKeyData key[2];
Relation rel;
HeapScanDesc scan;
- HeapTuple tuple;
+ HeapTuple tup;
ScanKeyInit(&key[0],
Anum_pg_class_relnamespace,
@@ -844,9 +844,9 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
rel = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- relations = lappend_oid(relations, HeapTupleGetOid(tuple));
+ relations = lappend_oid(relations, HeapTupleGetOid(tup));
}
heap_endscan(scan);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 027abd5..5f807ae 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2641,11 +2641,11 @@ IndexCheckExclusion(Relation heapRelation,
IndexInfo *indexInfo)
{
HeapScanDesc scan;
- HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
ExprState *predicate;
TupleTableSlot *slot;
+ HeapTuple heapTuple;
EState *estate;
ExprContext *econtext;
Snapshot snapshot;
@@ -2715,7 +2715,7 @@ IndexCheckExclusion(Relation heapRelation,
*/
check_exclusion_constraint(heapRelation,
indexRelation, indexInfo,
- &(heapTuple->t_self), values, isnull,
+ &(slot->tts_tid), values, isnull,
estate, true);
}
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..5fbef3f 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -149,7 +149,7 @@ void
RemoveConversionById(Oid conversionOid)
{
Relation rel;
- HeapTuple tuple;
+ HeapTuple tuple;
HeapScanDesc scan;
ScanKeyData scanKeyData;
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..52e0de7 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -172,7 +172,7 @@ DropSetting(Oid databaseid, Oid roleid)
Relation relsetting;
HeapScanDesc scan;
ScanKeyData keys[2];
- HeapTuple tup;
+ HeapTuple tup;
int numkeys = 0;
relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8..59abef4 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
Relation classRel;
ScanKeyData key[1];
HeapScanDesc scan;
- HeapTuple tuple;
+ HeapTuple tuple;
List *result = NIL;
classRel = heap_open(RelationRelationId, AccessShareLock);
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71..a3eb79d 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -378,7 +378,7 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
Relation rel;
HeapScanDesc scan;
ScanKeyData skey[2];
- HeapTuple tup;
+ HeapTuple tup;
int nkeys = 0;
rel = heap_open(SubscriptionRelRelationId, RowExclusiveLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e5..02a8dc5 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -39,7 +39,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
const char *funcname = "unique_key_recheck";
- HeapTuple new_row;
+ TupleTableSlot *newslot;
ItemPointerData tmptid;
Relation indexRel;
IndexInfo *indexInfo;
@@ -71,16 +71,16 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* Get the new data that was inserted/updated.
*/
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- new_row = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- new_row = trigdata->tg_newtuple;
+ newslot = trigdata->tg_newslot;
else
{
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("function \"%s\" must be fired for INSERT or UPDATE",
funcname)));
- new_row = NULL; /* keep compiler quiet */
+ newslot = NULL; /* keep compiler quiet */
}
/*
@@ -101,7 +101,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* it's possible the index entry has also been marked dead, and even
* removed.
*/
- tmptid = new_row->t_self;
+ tmptid = newslot->tts_tid;
if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
@@ -124,7 +124,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
*/
slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation));
- ExecStoreTuple(new_row, slot, InvalidBuffer, false);
+ ExecCopySlot(slot, newslot);
/*
* Typically the index won't have expressions, but if it does we need an
@@ -164,7 +164,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* correct even if t_self is now dead, because that is the TID the
* index will know about.
*/
- index_insert(indexRel, values, isnull, &(new_row->t_self),
+ index_insert(indexRel, values, isnull, &tmptid,
trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
indexInfo);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index fc5f4f6..5d3f22e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2068,13 +2068,9 @@ CopyTo(CopyState cstate)
if (cstate->rel)
{
- Datum *values;
- bool *nulls;
HeapScanDesc scandesc;
- HeapTuple tuple;
-
- values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
- nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
+ HeapTuple tuple;
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(cstate->rel));
scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
@@ -2083,18 +2079,15 @@ CopyTo(CopyState cstate)
{
CHECK_FOR_INTERRUPTS();
- /* Deconstruct the tuple ... faster than repeated heap_getattr */
- heap_deform_tuple(tuple, tupDesc, values, nulls);
+ ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+ slot_getallattrs(slot);
/* Format and send the data */
- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
+ CopyOneRowTo(cstate, HeapTupleGetOid(slot->tts_tuple), slot->tts_values, slot->tts_isnull);
processed++;
}
heap_endscan(scandesc);
-
- pfree(values);
- pfree(nulls);
}
else
{
@@ -2752,6 +2745,7 @@ CopyFrom(CopyState cstate)
heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
hi_options, bistate);
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
&(tuple->t_self),
@@ -2761,7 +2755,7 @@ CopyFrom(CopyState cstate)
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2910,11 +2904,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
+ ItemPointerCopy(&(bufferedTuples[i]->t_self), &(myslot->tts_tid));
recheckIndexes =
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2931,8 +2926,10 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
+ ItemPointerCopy(&(bufferedTuples[i]->t_self), &(myslot->tts_tid));
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539..e12c1db 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -590,6 +590,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
+
scan = heap_beginscan_catalog(rel, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 620704e..675b348 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1876,7 +1876,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
Relation relationRelation;
HeapScanDesc scan;
ScanKeyData scan_keys[1];
- HeapTuple tuple;
+ HeapTuple tuple;
MemoryContext private_context;
MemoryContext old;
List *relids = NIL;
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 7d57f97..062665e 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -678,7 +678,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
errmsg("new data for materialized view \"%s\" contains duplicate rows without any null columns",
RelationGetRelationName(matviewRel)),
errdetail("Row: %s",
- SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1))));
+ SPI_getvalue(SPI_tuptable->vals[0], 1))));
}
SetUserIdAndSecContext(relowner,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bb00858..c47a69f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8090,7 +8090,6 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
char *conbin;
Expr *origexpr;
ExprState *exprstate;
- TupleDesc tupdesc;
HeapScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
@@ -8127,8 +8126,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
exprstate = ExecPrepareExpr(origexpr, estate);
econtext = GetPerTupleExprContext(estate);
- tupdesc = RelationGetDescr(rel);
- slot = MakeSingleTupleTableSlot(tupdesc);
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
@@ -8157,7 +8155,6 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
MemoryContextSwitchTo(oldcxt);
heap_endscan(scan);
UnregisterSnapshot(snapshot);
- ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
}
@@ -8216,6 +8213,9 @@ validateForeignKeyConstraint(char *conname,
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+
+ ExecStoreTuple(tuple, slot, InvalidBuffer, false);
/*
* Make a call to the trigger function
@@ -8230,11 +8230,9 @@ validateForeignKeyConstraint(char *conname,
trigdata.type = T_TriggerData;
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
trigdata.tg_relation = rel;
- trigdata.tg_trigtuple = tuple;
- trigdata.tg_newtuple = NULL;
+ trigdata.tg_trigslot = slot;
+ trigdata.tg_newslot = NULL;
trigdata.tg_trigger = &trig;
- trigdata.tg_trigtuplebuf = scan->rs_cbuf;
- trigdata.tg_newtuplebuf = InvalidBuffer;
fcinfo.context = (Node *) &trigdata;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b502941..38b7215 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -88,7 +88,7 @@ static HeapTuple GetTupleForTrigger(EState *estate,
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
Trigger *trigger, TriggerEvent event,
Bitmapset *modifiedCols,
- HeapTuple oldtup, HeapTuple newtup);
+ TupleTableSlot *oldslot, TupleTableSlot *newslot);
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
FmgrInfo *finfo,
@@ -96,7 +96,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
MemoryContext per_tuple_context);
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
- HeapTuple oldtup, HeapTuple newtup,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
List *recheckIndexes, Bitmapset *modifiedCols,
TransitionCaptureState *transition_capture);
static void AfterTriggerEnlargeQueryState(void);
@@ -2298,12 +2298,10 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2359,10 +2357,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2373,21 +2370,22 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_INSERT))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, NULL, newtuple))
+ NULL, NULL, slot))
continue;
- LocTriggerData.tg_trigtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ oldtuple = newtuple;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
if (newtuple == NULL)
return NULL; /* "do nothing" */
+
+ if (oldtuple != newtuple) //hari
+ ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
}
if (newtuple != slottuple)
@@ -2411,7 +2409,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2419,7 +2417,7 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
- true, NULL, trigtuple,
+ true, NULL, slot,
recheckIndexes, NULL,
transition_capture);
}
@@ -2440,10 +2438,9 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_INSTEAD;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2454,21 +2451,22 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_INSERT))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, NULL, newtuple))
+ NULL, NULL, slot))
continue;
- LocTriggerData.tg_trigtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ oldtuple = newtuple;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
if (newtuple == NULL)
return NULL; /* "do nothing" */
+
+ if (oldtuple != newtuple) //hari
+ ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
}
if (newtuple != slottuple)
@@ -2508,12 +2506,10 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2557,7 +2553,7 @@ bool
ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple)
+ TupleTableSlot *trigslot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
bool result = true;
@@ -2565,28 +2561,31 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
HeapTuple trigtuple;
HeapTuple newtuple;
TupleTableSlot *newSlot;
+ TupleTableSlot *oldslot = trigslot;
int i;
- Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
- if (fdw_trigtuple == NULL)
+ Assert(PointerIsValid(trigslot) ^ ItemPointerIsValid(tupleid));
+ if (trigslot == NULL)
{
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
LockTupleExclusive, &newSlot);
if (trigtuple == NULL)
return false;
+
+ oldslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+ ExecStoreTuple(trigtuple, oldslot, InvalidBuffer, false);
}
else
- trigtuple = fdw_trigtuple;
+ trigtuple = ExecMaterializeSlot(trigslot);
LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2597,11 +2596,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
TRIGGER_TYPE_DELETE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, NULL))
+ NULL, oldslot, NULL))
continue;
- LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_trigslot = oldslot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
@@ -2616,8 +2614,9 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
if (newtuple != trigtuple)
heap_freetuple(newtuple);
}
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
+
+ if (trigslot != oldslot)
+ ExecDropSingleTupleTableSlot(oldslot);
return result;
}
@@ -2625,7 +2624,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
void
ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
+ TupleTableSlot *slot,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2634,33 +2633,38 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
(transition_capture && transition_capture->tcs_delete_old_table))
{
HeapTuple trigtuple;
+ TupleTableSlot *oldslot = slot;
- Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
- if (fdw_trigtuple == NULL)
+ Assert(PointerIsValid(slot) ^ ItemPointerIsValid(tupleid));
+ if (oldslot == NULL)
+ {
trigtuple = GetTupleForTrigger(estate,
NULL,
relinfo,
tupleid,
LockTupleExclusive,
NULL);
- else
- trigtuple = fdw_trigtuple;
+ oldslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+ ExecStoreTuple(trigtuple, oldslot, InvalidBuffer, false);
+ }
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
- true, trigtuple, NULL, NIL, NULL,
+ true, oldslot, NULL, NIL, NULL,
transition_capture);
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
+
+ if (oldslot != slot)
+ ExecDropSingleTupleTableSlot(oldslot);
}
}
bool
ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple)
+ TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
TriggerData LocTriggerData;
HeapTuple rettuple;
+ HeapTuple trigtuple = ExecMaterializeSlot(slot);
int i;
LocTriggerData.type = T_TriggerData;
@@ -2668,10 +2672,9 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_INSTEAD;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2682,11 +2685,10 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_DELETE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, NULL))
+ NULL, slot, NULL))
continue;
- LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigger = trigger;
rettuple = ExecCallTriggerFunc(&LocTriggerData,
i,
@@ -2694,10 +2696,14 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
if (rettuple == NULL)
+ {
+ ExecDropSingleTupleTableSlot(slot);
return false; /* Delete was suppressed */
+ }
if (rettuple != trigtuple)
heap_freetuple(rettuple);
}
+
return true;
}
@@ -2722,12 +2728,10 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2773,16 +2777,16 @@ TupleTableSlot *
ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
+ TupleTableSlot *trigslot,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
HeapTuple slottuple = ExecMaterializeSlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
- HeapTuple trigtuple;
HeapTuple oldtuple;
TupleTableSlot *newSlot;
+ TupleTableSlot *oldSlot = trigslot;
int i;
Bitmapset *updatedCols;
LockTupleMode lockmode;
@@ -2790,18 +2794,22 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, relinfo);
- Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
- if (fdw_trigtuple == NULL)
+ Assert(PointerIsValid(trigslot) ^ ItemPointerIsValid(tupleid));
+ if (trigslot == NULL)
{
+ HeapTuple trigtuple;
+
/* get a copy of the on-disk tuple we are planning to update */
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
lockmode, &newSlot);
if (trigtuple == NULL)
return NULL; /* cancel the update action */
+
+ oldSlot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+ ExecStoreTuple(trigtuple, oldSlot, InvalidBuffer, true);
}
else
{
- trigtuple = fdw_trigtuple;
newSlot = NULL;
}
@@ -2842,30 +2850,32 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TRIGGER_TYPE_UPDATE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- updatedCols, trigtuple, newtuple))
+ updatedCols, oldSlot, slot))
continue;
- LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_newtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ oldtuple = newtuple;
+ LocTriggerData.tg_trigslot = oldSlot;
+ LocTriggerData.tg_newslot = slot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
if (newtuple == NULL)
{
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
+ if (oldSlot != trigslot)
+ ExecDropSingleTupleTableSlot(oldSlot);
+
return NULL; /* "do nothing" */
}
+
+ if (oldtuple != newtuple) //hari
+ ExecStoreTuple(newtuple, slot, InvalidBuffer, true);
}
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
+
+ if (oldSlot != trigslot)
+ ExecDropSingleTupleTableSlot(oldSlot);
if (newtuple != slottuple)
{
@@ -2889,8 +2899,8 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
void
ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
- HeapTuple newtuple,
+ TupleTableSlot *oldslot,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
@@ -2902,30 +2912,35 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
transition_capture->tcs_update_new_table)))
{
HeapTuple trigtuple;
+ TupleTableSlot *trigslot = oldslot;
- Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
- if (fdw_trigtuple == NULL)
+ Assert(PointerIsValid(oldslot) ^ ItemPointerIsValid(tupleid));
+ if (oldslot == NULL)
+ {
trigtuple = GetTupleForTrigger(estate,
NULL,
relinfo,
tupleid,
LockTupleExclusive,
NULL);
- else
- trigtuple = fdw_trigtuple;
+
+ trigslot = MakeSingleTupleTableSlot(RelationGetDescr(relinfo->ri_RelationDesc));
+ ExecStoreTuple(trigtuple, trigslot, InvalidBuffer, false);
+ }
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
- true, trigtuple, newtuple, recheckIndexes,
+ true, trigslot, slot, recheckIndexes,
GetUpdatedColumns(relinfo, estate),
transition_capture);
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
+
+ if (oldslot != trigslot)
+ ExecDropSingleTupleTableSlot(trigslot);
}
}
TupleTableSlot *
ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, TupleTableSlot *slot)
+ TupleTableSlot *oldslot, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
HeapTuple slottuple = ExecMaterializeSlot(slot);
@@ -2951,13 +2966,12 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_UPDATE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, newtuple))
+ NULL, oldslot, slot))
continue;
- LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_newtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ oldtuple = newtuple;
+ LocTriggerData.tg_trigslot = oldslot;
+ LocTriggerData.tg_newslot = slot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
@@ -2968,6 +2982,8 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
heap_freetuple(oldtuple);
if (newtuple == NULL)
return NULL; /* "do nothing" */
+
+ ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
}
if (newtuple != slottuple)
@@ -3007,12 +3023,10 @@ ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
- LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -3200,7 +3214,7 @@ static bool
TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
Trigger *trigger, TriggerEvent event,
Bitmapset *modifiedCols,
- HeapTuple oldtup, HeapTuple newtup)
+ TupleTableSlot *oldslot, TupleTableSlot *newslot)
{
/* Check replication-role-dependent enable state */
if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
@@ -3245,8 +3259,8 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
ExprState **predicate;
ExprContext *econtext;
- TupleTableSlot *oldslot = NULL;
- TupleTableSlot *newslot = NULL;
+ TupleTableSlot *old_localslot = NULL;
+ TupleTableSlot *new_localslot = NULL;
MemoryContext oldContext;
int i;
@@ -3290,7 +3304,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
* These slots can be shared across the whole estate, but be careful
* that they have the current resultrel's tupdesc.
*/
- if (HeapTupleIsValid(oldtup))
+ if (PointerIsValid(oldslot))
{
if (estate->es_trig_oldtup_slot == NULL)
{
@@ -3298,12 +3312,12 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
estate->es_trig_oldtup_slot = ExecInitExtraTupleSlot(estate);
MemoryContextSwitchTo(oldContext);
}
- oldslot = estate->es_trig_oldtup_slot;
- if (oldslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(oldslot, tupdesc);
- ExecStoreTuple(oldtup, oldslot, InvalidBuffer, false);
+ old_localslot = estate->es_trig_oldtup_slot;
+ if (old_localslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(old_localslot, tupdesc);
+ ExecStoreTuple(oldslot->tts_tuple, old_localslot, InvalidBuffer, false);
}
- if (HeapTupleIsValid(newtup))
+ if (PointerIsValid(newslot))
{
if (estate->es_trig_newtup_slot == NULL)
{
@@ -3311,18 +3325,18 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
estate->es_trig_newtup_slot = ExecInitExtraTupleSlot(estate);
MemoryContextSwitchTo(oldContext);
}
- newslot = estate->es_trig_newtup_slot;
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreTuple(newtup, newslot, InvalidBuffer, false);
+ new_localslot = estate->es_trig_newtup_slot;
+ if (new_localslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(new_localslot, tupdesc);
+ ExecStoreTuple(newslot->tts_tuple, new_localslot, InvalidBuffer, false);
}
/*
* Finally evaluate the expression, making the old and/or new tuples
* available as INNER_VAR/OUTER_VAR respectively.
*/
- econtext->ecxt_innertuple = oldslot;
- econtext->ecxt_outertuple = newslot;
+ econtext->ecxt_innertuple = old_localslot;
+ econtext->ecxt_outertuple = new_localslot;
if (!ExecQual(*predicate, econtext))
return false;
}
@@ -3900,6 +3914,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
TriggerData LocTriggerData;
HeapTupleData tuple1;
HeapTupleData tuple2;
+ HeapTuple oldtuple = NULL;
+ HeapTuple newtuple = NULL;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -3960,15 +3976,11 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_trigslot = trig_tuple_slot1;
- LocTriggerData.tg_newtuple =
+ LocTriggerData.tg_newslot =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
- TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ TRIGGER_EVENT_UPDATE) ? trig_tuple_slot2 : NULL;
break;
@@ -3978,13 +3990,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
- LocTriggerData.tg_trigtuplebuf = buffer1;
+ LocTriggerData.tg_trigslot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+ ExecStoreTuple(&tuple1, LocTriggerData.tg_trigslot, buffer1, false);
}
else
{
- LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_trigslot = NULL;
}
/* don't touch ctid2 if not there */
@@ -3995,13 +4006,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
- LocTriggerData.tg_newtuplebuf = buffer2;
+ LocTriggerData.tg_newslot = MakeSingleTupleTableSlot(RelationGetDescr(rel));
+ ExecStoreTuple(&tuple2, LocTriggerData.tg_newslot, buffer2, false);
}
else
{
- LocTriggerData.tg_newtuple = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_newslot = NULL;
}
}
@@ -4039,6 +4049,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
}
}
+ if (LocTriggerData.tg_trigslot != NULL)
+ oldtuple = ExecMaterializeSlot(LocTriggerData.tg_trigslot);
+
+ if (LocTriggerData.tg_newslot != NULL)
+ newtuple = ExecMaterializeSlot(LocTriggerData.tg_newslot);
+
/*
* Setup the remaining trigger information
*/
@@ -4059,10 +4075,18 @@ AfterTriggerExecute(AfterTriggerEvent event,
NULL,
per_tuple_context);
if (rettuple != NULL &&
- rettuple != LocTriggerData.tg_trigtuple &&
- rettuple != LocTriggerData.tg_newtuple)
+ rettuple != oldtuple &&
+ rettuple != newtuple)
heap_freetuple(rettuple);
+ if ((LocTriggerData.tg_trigslot != NULL) &&
+ (LocTriggerData.tg_trigslot != trig_tuple_slot1))
+ ExecDropSingleTupleTableSlot(LocTriggerData.tg_trigslot);
+
+ if ((LocTriggerData.tg_newslot != NULL) &&
+ (LocTriggerData.tg_newslot != trig_tuple_slot2))
+ ExecDropSingleTupleTableSlot(LocTriggerData.tg_newslot);
+
/*
* Release buffers
*/
@@ -5221,7 +5245,7 @@ AfterTriggerPendingOnRel(Oid relid)
static void
AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
- HeapTuple oldtup, HeapTuple newtup,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
List *recheckIndexes, Bitmapset *modifiedCols,
TransitionCaptureState *transition_capture)
{
@@ -5265,25 +5289,27 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
{
Tuplestorestate *old_tuplestore;
- Assert(oldtup != NULL);
+ Assert(oldslot != NULL);
+ Assert(oldslot->tts_tuple != NULL);
old_tuplestore = transition_capture->tcs_old_tuplestore;
if (map != NULL)
{
- HeapTuple converted = do_convert_tuple(oldtup, map);
+ HeapTuple converted = do_convert_tuple(oldslot->tts_tuple, map);
tuplestore_puttuple(old_tuplestore, converted);
pfree(converted);
}
else
- tuplestore_puttuple(old_tuplestore, oldtup);
+ tuplestore_puttuple(old_tuplestore, oldslot->tts_tuple);
}
if ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
(event == TRIGGER_EVENT_UPDATE && update_new_table))
{
Tuplestorestate *new_tuplestore;
- Assert(newtup != NULL);
+ Assert(newslot != NULL);
+ Assert(newslot->tts_tuple != NULL);
if (event == TRIGGER_EVENT_INSERT)
new_tuplestore = transition_capture->tcs_insert_tuplestore;
else
@@ -5293,13 +5319,13 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tuplestore_puttuple(new_tuplestore, original_insert_tuple);
else if (map != NULL)
{
- HeapTuple converted = do_convert_tuple(newtup, map);
+ HeapTuple converted = do_convert_tuple(newslot->tts_tuple, map);
tuplestore_puttuple(new_tuplestore, converted);
pfree(converted);
}
else
- tuplestore_puttuple(new_tuplestore, newtup);
+ tuplestore_puttuple(new_tuplestore, newslot->tts_tuple);
}
/* If transition tables are the only reason we're here, return. */
@@ -5323,15 +5349,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_INSERT;
if (row_trigger)
{
- Assert(oldtup == NULL);
- Assert(newtup != NULL);
- ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
+ Assert(oldslot == NULL);
+ Assert(newslot != NULL);
+ ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
@@ -5340,15 +5366,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_DELETE;
if (row_trigger)
{
- Assert(oldtup != NULL);
- Assert(newtup == NULL);
- ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
+ Assert(oldslot != NULL);
+ Assert(newslot == NULL);
+ ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
@@ -5357,23 +5383,23 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_UPDATE;
if (row_trigger)
{
- Assert(oldtup != NULL);
- Assert(newtup != NULL);
- ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
- ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
+ Assert(oldslot != NULL);
+ Assert(newslot != NULL);
+ ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
+ ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
break;
case TRIGGER_EVENT_TRUNCATE:
tgtype_event = TRIGGER_TYPE_TRUNCATE;
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
break;
@@ -5400,7 +5426,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, event,
- modifiedCols, oldtup, newtup))
+ modifiedCols, oldslot, newslot))
continue;
if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
@@ -5429,7 +5455,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
case RI_TRIGGER_PK:
/* Update on trigger's PK table */
if (!RI_FKey_pk_upd_check_required(trigger, rel,
- oldtup, newtup))
+ oldslot, newslot))
{
/* skip queuing this event */
continue;
@@ -5439,7 +5465,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
case RI_TRIGGER_FK:
/* Update on trigger's FK table */
if (!RI_FKey_fk_upd_check_required(trigger, rel,
- oldtup, newtup))
+ oldslot, newslot))
{
/* skip queuing this event */
continue;
@@ -5487,10 +5513,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
*/
if (fdw_tuplestore)
{
- if (oldtup != NULL)
- tuplestore_puttuple(fdw_tuplestore, oldtup);
- if (newtup != NULL)
- tuplestore_puttuple(fdw_tuplestore, newtup);
+ if (oldslot != NULL)
+ tuplestore_puttuple(fdw_tuplestore, oldslot->tts_tuple);
+ if (newslot != NULL)
+ tuplestore_puttuple(fdw_tuplestore, newslot->tts_tuple);
}
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index bc53d07..ab4a6c5 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -409,14 +409,14 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
-
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -482,10 +482,11 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
estate, false, NULL,
NIL);
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
&searchslot->tts_tuple->t_self,
- NULL, tuple, recheckIndexes, NULL);
+ NULL, slot, recheckIndexes, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 7ae70a8..fabb628 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -116,6 +116,7 @@ MakeTupleTableSlot(void)
slot->tts_shouldFree = false;
slot->tts_shouldFreeMin = false;
slot->tts_tuple = NULL;
+ ItemPointerSetInvalid(&(slot->tts_tid));
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
slot->tts_buffer = InvalidBuffer;
@@ -369,6 +370,8 @@ ExecStoreTuple(HeapTuple tuple,
IncrBufferRefCount(buffer);
}
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
+
return slot;
}
@@ -427,6 +430,63 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
return slot;
}
+/*
+ * heap_modify_slot_by_cols
+ * form a new tuple from an old tuple and a set of replacement values.
+ *
+ * This is like heap_modify_tuple, except that instead of specifying which
+ * column(s) to replace by a boolean map, an array of target column numbers
+ * is used. This is often more convenient when a fixed number of columns
+ * are to be replaced. The replCols, replValues, and replIsnull arrays must
+ * be of length nCols. Target column numbers are indexed from 1.
+ *
+ * The result is allocated in the current memory context.
+ */
+TupleTableSlot*
+heap_modify_slot_by_cols(TupleTableSlot *slot,
+ int nCols,
+ int *replCols,
+ Datum *replValues,
+ bool *replIsnull)
+{
+ int numberOfAttributes = slot->tts_tupleDescriptor->natts;
+ HeapTuple newTuple;
+ int i;
+
+ slot_getallattrs(slot);
+
+ for (i = 0; i < nCols; i++)
+ {
+ int attnum = replCols[i];
+
+ if (attnum <= 0 || attnum > numberOfAttributes)
+ elog(ERROR, "invalid column number %d", attnum);
+ slot->tts_values[attnum - 1] = replValues[i];
+ slot->tts_isnull[attnum - 1] = replIsnull[i];
+ }
+
+ /*
+ * create a new tuple from the values and isnull arrays
+ */
+ newTuple = heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
+
+ /*
+ * copy the identification info of the old tuple: t_ctid, t_self, and OID
+ * (if any)
+ */
+ newTuple->t_data->t_ctid = slot->tts_tuple->t_data->t_ctid;
+ newTuple->t_self = slot->tts_tuple->t_self;
+ newTuple->t_tableOid = slot->tts_tuple->t_tableOid;
+ if (slot->tts_tupleDescriptor->tdhasoid)
+ HeapTupleSetOid(newTuple, HeapTupleGetOid(slot->tts_tuple));
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(slot->tts_tuple);
+
+ slot->tts_tuple = newTuple;
+ return slot;
+}
+
/* --------------------------------
* ExecClearTuple
*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 8d17425..387c059 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -557,6 +557,7 @@ ExecInsert(ModifyTableState *mtstate,
HEAP_INSERT_SPECULATIVE,
NULL);
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
/* insert index entries for tuple */
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
estate, true, &specConflict,
@@ -602,6 +603,7 @@ ExecInsert(ModifyTableState *mtstate,
estate->es_output_cid,
0, NULL);
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
@@ -618,7 +620,7 @@ ExecInsert(ModifyTableState *mtstate,
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -669,7 +671,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ TupleTableSlot *oldslot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -694,7 +696,7 @@ ExecDelete(ModifyTableState *mtstate,
bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- tupleid, oldtuple);
+ tupleid, oldslot);
if (!dodelete) /* "do nothing" */
return NULL;
@@ -706,8 +708,8 @@ ExecDelete(ModifyTableState *mtstate,
{
bool dodelete;
- Assert(oldtuple != NULL);
- dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
+ Assert(oldslot != NULL);
+ dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldslot);
if (!dodelete) /* "do nothing" */
return NULL;
@@ -844,8 +846,11 @@ ldelete:;
if (canSetTag)
(estate->es_processed)++;
+ if (oldslot != NULL)
+ ItemPointerCopy(tupleid, &(oldslot->tts_tid));
+
/* AFTER ROW DELETE Triggers */
- ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
+ ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldslot,
mtstate->mt_transition_capture);
/* Process RETURNING if present */
@@ -868,10 +873,12 @@ ldelete:;
else
{
slot = estate->es_trig_tuple_slot;
- if (oldtuple != NULL)
+ if (oldslot != NULL)
{
- deltuple = *oldtuple;
- delbuffer = InvalidBuffer;
+ ExecCopySlot(slot, oldslot);
+
+ if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+ ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
}
else
{
@@ -879,11 +886,11 @@ ldelete:;
if (!heap_fetch(resultRelationDesc, SnapshotAny,
&deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
- }
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+ ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
+ ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ }
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -929,7 +936,7 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ TupleTableSlot *oldslot,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
@@ -966,7 +973,7 @@ ExecUpdate(ModifyTableState *mtstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- tupleid, oldtuple, slot);
+ tupleid, oldslot, slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@@ -980,7 +987,7 @@ ExecUpdate(ModifyTableState *mtstate,
resultRelInfo->ri_TrigDesc->trig_update_instead_row)
{
slot = ExecIRUpdateTriggers(estate, resultRelInfo,
- oldtuple, slot);
+ oldslot, slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@@ -1138,6 +1145,7 @@ lreplace:;
* All we do here is insert new index tuples. -cim 9/27/89
*/
+ ItemPointerCopy(&(tuple->t_self), &(slot->tts_tid));
/*
* insert index entries for tuple
*
@@ -1155,7 +1163,7 @@ lreplace:;
(estate->es_processed)++;
/* AFTER ROW UPDATE Triggers */
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
+ ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldslot, slot,
recheckIndexes,
mtstate->mt_transition_capture);
@@ -1550,6 +1558,7 @@ ExecModifyTable(ModifyTableState *node)
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
HeapTuple oldtuple;
+ TupleTableSlot *oldslot = NULL;
/*
* This should NOT get called during EvalPlanQual; we should have passed a
@@ -1659,7 +1668,8 @@ ExecModifyTable(ModifyTableState *node)
}
EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
- slot = planSlot;
+ slot = MakeSingleTupleTableSlot(planSlot->tts_tupleDescriptor);
+ ExecCopySlot(slot, planSlot);
oldtuple = NULL;
if (junkfilter != NULL)
@@ -1721,6 +1731,10 @@ ExecModifyTable(ModifyTableState *node)
RelationGetRelid(resultRelInfo->ri_RelationDesc);
oldtuple = &oldtupdata;
+
+ ItemPointerCopy(tupleid, &(oldslot->tts_tid));
+ oldslot = MakeSingleTupleTableSlot(RelationGetDescr(resultRelInfo->ri_RelationDesc));
+ ExecStoreTuple(oldtuple, oldslot, InvalidBuffer, false);
}
else
Assert(relkind == RELKIND_FOREIGN_TABLE);
@@ -1741,11 +1755,11 @@ ExecModifyTable(ModifyTableState *node)
estate, node->canSetTag);
break;
case CMD_UPDATE:
- slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
+ slot = ExecUpdate(node, tupleid, oldslot, slot, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
case CMD_DELETE:
- slot = ExecDelete(node, tupleid, oldtuple, planSlot,
+ slot = ExecDelete(node, tupleid, oldslot, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
default:
@@ -1753,6 +1767,9 @@ ExecModifyTable(ModifyTableState *node)
break;
}
+ if (oldslot)
+ ExecDropSingleTupleTableSlot(oldslot);
+
/*
* If we got a RETURNING result, return it to caller. We'll continue
* the work on next call.
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index cd00a6d..79ffa52 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -23,6 +23,7 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi_priv.h"
+#include "executor/tuptable.h"
#include "miscadmin.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
@@ -757,6 +758,72 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
return mtuple;
}
+TupleTableSlot *
+SPI_modifyslot(TupleTableSlot *slot, int natts, int *attnum,
+ Datum *Values, const char *Nulls)
+{
+ MemoryContext oldcxt;
+ HeapTuple mtuple;
+ int numberOfAttributes;
+ int i;
+
+ if (slot == NULL || natts < 0 || attnum == NULL || Values == NULL)
+ {
+ SPI_result = SPI_ERROR_ARGUMENT;
+ return NULL;
+ }
+
+ if (_SPI_current == NULL)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return NULL;
+ }
+
+ oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
+
+ SPI_result = 0;
+
+ numberOfAttributes = slot->tts_tupleDescriptor->natts;
+
+ /* replace values and nulls */
+ for (i = 0; i < natts; i++)
+ {
+ if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
+ break;
+ slot->tts_values[attnum[i] - 1] = Values[i];
+ slot->tts_isnull[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? true : false;
+ }
+
+ if (i == natts) /* no errors in *attnum */
+ {
+ mtuple = heap_form_tuple(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
+
+ /*
+ * copy the identification info of the old tuple: t_ctid, t_self, and
+ * OID (if any)
+ */
+ mtuple->t_data->t_ctid = slot->tts_tuple->t_data->t_ctid;
+ mtuple->t_self = slot->tts_tuple->t_self;
+ mtuple->t_tableOid = slot->tts_tuple->t_tableOid;
+ if (slot->tts_tupleDescriptor->tdhasoid)
+ HeapTupleSetOid(mtuple, HeapTupleGetOid(slot->tts_tuple));
+ }
+ else
+ {
+ mtuple = NULL;
+ SPI_result = SPI_ERROR_NOATTRIBUTE;
+ }
+
+ MemoryContextSwitchTo(oldcxt);
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(slot->tts_tuple);
+
+ slot->tts_tuple = mtuple;
+
+ return slot;
+}
+
int
SPI_fnumber(TupleDesc tupdesc, const char *fname)
{
@@ -801,7 +868,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
}
char *
-SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
+SPI_getvalue(TupleTableSlot *slot, int fnumber)
{
Datum val;
bool isnull;
@@ -811,19 +878,19 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
SPI_result = 0;
- if (fnumber > tupdesc->natts || fnumber == 0 ||
+ if (fnumber > slot->tts_tupleDescriptor->natts || fnumber == 0 ||
fnumber <= FirstLowInvalidHeapAttributeNumber)
{
SPI_result = SPI_ERROR_NOATTRIBUTE;
return NULL;
}
- val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
+ val = slot_getattr(slot, fnumber, &isnull);
if (isnull)
return NULL;
if (fnumber > 0)
- typoid = tupdesc->attrs[fnumber - 1]->atttypid;
+ typoid = slot->tts_tupleDescriptor->attrs[fnumber - 1]->atttypid;
else
typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
@@ -833,11 +900,11 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
}
Datum
-SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
+SPI_getbinval(TupleTableSlot *slot, int fnumber, bool *isnull)
{
SPI_result = 0;
- if (fnumber > tupdesc->natts || fnumber == 0 ||
+ if (fnumber > slot->tts_tupleDescriptor->natts || fnumber == 0 ||
fnumber <= FirstLowInvalidHeapAttributeNumber)
{
SPI_result = SPI_ERROR_NOATTRIBUTE;
@@ -845,7 +912,7 @@ SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
return (Datum) NULL;
}
- return heap_getattr(tuple, fnumber, tupdesc, isnull);
+ return slot_getattr(slot, fnumber, isnull);
}
char *
@@ -1681,7 +1748,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (TupleTableSlot **) palloc(tuptable->alloced * sizeof(TupleTableSlot *));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1697,6 +1764,7 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
+ TupleTableSlot *dstslot;
if (_SPI_current == NULL)
elog(ERROR, "spi_printtup called while not connected to SPI");
@@ -1712,12 +1780,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (TupleTableSlot **) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(TupleTableSlot *));
}
- tuptable->vals[tuptable->alloced - tuptable->free] =
- ExecCopySlotTuple(slot);
+ dstslot = MakeSingleTupleTableSlot(tuptable->tupdesc);
+ tuptable->vals[tuptable->alloced - tuptable->free] = ExecCopySlot(dstslot, slot);
(tuptable->free)--;
MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 0d48dfa..4a01fbd 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -726,6 +726,7 @@ apply_handle_update(StringInfo s)
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ ItemPointerCopy(&(localslot->tts_tuple->t_self), &(remoteslot->tts_tid));
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6..01714f4 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -194,7 +194,7 @@ static int ri_constraint_cache_valid_count = 0;
* ----------
*/
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo);
static Datum ri_restrict_del(TriggerData *trigdata, bool is_no_action);
static Datum ri_restrict_upd(TriggerData *trigdata, bool is_no_action);
@@ -207,12 +207,12 @@ static void ri_GenerateQual(StringInfo buf,
const char *rightop, Oid rightoptype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(HeapTuple tup,
+static int ri_NullCheck(TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static void ri_BuildQueryKey(RI_QueryKey *key,
const RI_ConstraintInfo *riinfo,
int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue);
@@ -234,14 +234,13 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
bool detectNewRows, int expect_OK);
-static void ri_ExtractValues(Relation rel, HeapTuple tup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk,
- Datum *vals, char *nulls);
+static void ri_ExtractValues(TupleTableSlot *slot, const RI_ConstraintInfo *riinfo,
+ bool rel_is_pk, Datum *vals, char *nulls);
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot* violator, TupleDesc tupdesc,
int queryno, bool spi_err);
@@ -257,8 +256,7 @@ RI_FKey_check(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- Buffer new_row_buf;
+ TupleTableSlot *newslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -270,15 +268,9 @@ RI_FKey_check(TriggerData *trigdata)
trigdata->tg_relation, false);
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- {
- new_row = trigdata->tg_newtuple;
- new_row_buf = trigdata->tg_newtuplebuf;
- }
+ newslot = trigdata->tg_newslot;
else
- {
- new_row = trigdata->tg_trigtuple;
- new_row_buf = trigdata->tg_trigtuplebuf;
- }
+ newslot = trigdata->tg_trigslot;
/*
* We should not even consider checking the row if it is no longer valid,
@@ -288,13 +280,8 @@ RI_FKey_check(TriggerData *trigdata)
* and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller
* should be holding pin, but not lock.
*/
- LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
- {
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+ if (!HeapTupleSatisfiesVisibility(newslot->tts_tuple, SnapshotSelf, InvalidBuffer))
return PointerGetDatum(NULL);
- }
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
/*
* Get the relation descriptors of the FK and PK tables.
@@ -310,7 +297,7 @@ RI_FKey_check(TriggerData *trigdata)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
- switch (ri_NullCheck(new_row, riinfo, false))
+ switch (ri_NullCheck(newslot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
@@ -440,7 +427,7 @@ RI_FKey_check(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- NULL, new_row,
+ NULL, newslot,
false,
SPI_OK_SELECT);
@@ -508,7 +495,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
*/
static bool
ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo)
{
SPIPlanPtr qplan;
@@ -517,7 +504,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
bool result;
/* Only called for non-null rows */
- Assert(ri_NullCheck(old_row, riinfo, true) == RI_KEYS_NONE_NULL);
+ Assert(ri_NullCheck(oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
@@ -575,8 +562,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/
result = ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* treat like update */
+ oldslot, NULL,
+ true, /* treat like update */
SPI_OK_SELECT);
if (SPI_finish() != SPI_OK_FINISH)
@@ -645,7 +632,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -664,7 +651,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
*/
fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -677,7 +664,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -704,7 +691,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
* allow another row to be substituted.
*/
if (is_no_action &&
- ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+ ri_Check_Pk_Match(pk_rel, fk_rel, oldslot, riinfo))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -767,8 +754,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_SELECT);
if (SPI_finish() != SPI_OK_FINISH)
@@ -857,8 +844,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *newslot;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -878,8 +865,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
*/
fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_newslot;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -892,7 +879,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -915,7 +902,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
/*
* No need to check anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -928,7 +915,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
* allow another row to be substituted.
*/
if (is_no_action &&
- ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+ ri_Check_Pk_Match(pk_rel, fk_rel, oldslot, riinfo))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -990,8 +977,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_SELECT);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1034,7 +1021,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1058,7 +1045,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1071,7 +1058,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1146,8 +1133,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_DELETE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1190,8 +1177,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *newslot;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1217,8 +1204,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_newslot;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1231,7 +1218,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1254,7 +1241,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -1327,8 +1314,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, new_row,
- true, /* must detect new rows */
+ oldslot, newslot,
+ true, /* must detect new rows */
SPI_OK_UPDATE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1371,7 +1358,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1395,7 +1382,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1408,7 +1395,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1492,8 +1479,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_UPDATE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1536,8 +1523,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *newslot;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1561,8 +1548,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_newslot;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1575,7 +1562,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1598,7 +1585,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -1668,8 +1655,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_UPDATE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1712,7 +1699,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -1735,7 +1722,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1748,7 +1735,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1834,8 +1821,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_UPDATE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -1892,8 +1879,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *newslot;
+ TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -1916,8 +1903,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ newslot = trigdata->tg_newslot;
+ oldslot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1930,7 +1917,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
*/
case FKCONSTR_MATCH_SIMPLE:
case FKCONSTR_MATCH_FULL:
- switch (ri_NullCheck(old_row, riinfo, true))
+ switch (ri_NullCheck(oldslot, riinfo, true))
{
case RI_KEYS_ALL_NULL:
case RI_KEYS_SOME_NULL:
@@ -1953,7 +1940,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -2025,8 +2012,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
- true, /* must detect new rows */
+ oldslot, NULL,
+ true, /* must detect new rows */
SPI_OK_UPDATE);
if (SPI_finish() != SPI_OK_FINISH)
@@ -2082,7 +2069,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
*/
bool
RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *oldslot, TupleTableSlot *newslot)
{
const RI_ConstraintInfo *riinfo;
@@ -2100,11 +2087,11 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
* If any old key value is NULL, the row could not have been
* referenced by an FK row, so no check is needed.
*/
- if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(oldslot, riinfo, true) != RI_KEYS_NONE_NULL)
return false;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
return false;
/* Else we need to fire the trigger. */
@@ -2139,7 +2126,7 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
*/
bool
RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *oldslot, TupleTableSlot *newslot)
{
const RI_ConstraintInfo *riinfo;
@@ -2156,7 +2143,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* If any new key value is NULL, the row must satisfy the
* constraint, so no check is needed.
*/
- if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(newslot, riinfo, false) != RI_KEYS_NONE_NULL)
return false;
/*
@@ -2167,11 +2154,11 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(oldslot->tts_tuple->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -2187,7 +2174,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* invalidated before the constraint is to be checked, but we
* should queue the event to apply the check later.
*/
- switch (ri_NullCheck(new_row, riinfo, false))
+ switch (ri_NullCheck(newslot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
return false;
@@ -2205,11 +2192,11 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(oldslot->tts_tuple->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -2458,7 +2445,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
/* Did we find a tuple violating the constraint? */
if (SPI_processed > 0)
{
- HeapTuple tuple = SPI_tuptable->vals[0];
+ TupleTableSlot *slot = SPI_tuptable->vals[0];
TupleDesc tupdesc = SPI_tuptable->tupdesc;
RI_ConstraintInfo fake_riinfo;
@@ -2481,7 +2468,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* disallows partially-null FK rows.
*/
if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
- ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
+ ri_NullCheck(slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@@ -2498,7 +2485,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
*/
ri_ReportViolation(&fake_riinfo,
pk_rel, fk_rel,
- tuple, tupdesc,
+ slot, tupdesc,
RI_PLAN_CHECK_LOOKUPPK, false);
}
@@ -3038,11 +3025,10 @@ static bool
ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
bool detectNewRows, int expect_OK)
{
- Relation query_rel,
- source_rel;
+ Relation query_rel;
bool source_is_pk;
Snapshot test_snapshot;
Snapshot crosscheck_snapshot;
@@ -3070,28 +3056,22 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
* need some less klugy way to determine this.
*/
if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
- {
- source_rel = fk_rel;
source_is_pk = false;
- }
else
- {
- source_rel = pk_rel;
source_is_pk = true;
- }
/* Extract the parameters to be passed into the query */
- if (new_tuple)
+ if (newslot)
{
- ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(newslot, riinfo, source_is_pk,
vals, nulls);
- if (old_tuple)
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ if (oldslot)
+ ri_ExtractValues(oldslot, riinfo, source_is_pk,
vals + riinfo->nkeys, nulls + riinfo->nkeys);
}
else
{
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(oldslot, riinfo, source_is_pk,
vals, nulls);
}
@@ -3149,7 +3129,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
if (expect_OK >= 0 && spi_result != expect_OK)
ri_ReportViolation(riinfo,
pk_rel, fk_rel,
- new_tuple ? new_tuple : old_tuple,
+ newslot ? newslot : oldslot,
NULL,
qkey->constr_queryno, true);
@@ -3159,7 +3139,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
ri_ReportViolation(riinfo,
pk_rel, fk_rel,
- new_tuple ? new_tuple : old_tuple,
+ newslot ? newslot : oldslot,
NULL,
qkey->constr_queryno, false);
@@ -3170,11 +3150,9 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
* Extract fields from a tuple into Datum/nulls arrays
*/
static void
-ri_ExtractValues(Relation rel, HeapTuple tup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk,
- Datum *vals, char *nulls)
+ri_ExtractValues(TupleTableSlot *slot, const RI_ConstraintInfo *riinfo,
+ bool rel_is_pk, Datum *vals, char *nulls)
{
- TupleDesc tupdesc = rel->rd_att;
const int16 *attnums;
int i;
bool isnull;
@@ -3186,8 +3164,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
for (i = 0; i < riinfo->nkeys; i++)
{
- vals[i] = heap_getattr(tup, attnums[i], tupdesc,
- &isnull);
+ vals[i] = slot_getattr(slot, attnums[i], &isnull);
nulls[i] = isnull ? 'n' : ' ';
}
}
@@ -3204,7 +3181,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
static void
ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot *violator, TupleDesc tupdesc,
int queryno, bool spi_err)
{
StringInfoData key_names;
@@ -3292,7 +3269,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
*val;
name = SPI_fname(tupdesc, fnum);
- val = SPI_getvalue(violator, tupdesc, fnum);
+ val = SPI_getvalue(violator, fnum);
if (!val)
val = "null";
@@ -3345,7 +3322,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
* ----------
*/
static int
-ri_NullCheck(HeapTuple tup,
+ri_NullCheck(TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
const int16 *attnums;
@@ -3360,7 +3337,7 @@ ri_NullCheck(HeapTuple tup,
for (i = 0; i < riinfo->nkeys; i++)
{
- if (heap_attisnull(tup, attnums[i]))
+ if (slot_attisnull(slot, attnums[i]))
nonenull = false;
else
allnull = false;
@@ -3511,10 +3488,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
* ----------
*/
static bool
-ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
- TupleDesc tupdesc = RelationGetDescr(rel);
const int16 *attnums;
const Oid *eq_oprs;
int i;
@@ -3539,14 +3515,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
/*
* Get one attribute's oldvalue. If it is NULL - they're not equal.
*/
- oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
+ oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
if (isnull)
return false;
/*
* Get one attribute's newvalue. If it is NULL - they're not equal.
*/
- newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
+ newvalue = slot_getattr(newslot, attnums[i], &isnull);
if (isnull)
return false;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5cfb916..54a31ce 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -362,9 +362,8 @@ static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
deparse_namespace *save_dpns);
static void pop_ancestor_plan(deparse_namespace *dpns,
deparse_namespace *save_dpns);
-static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
- int prettyFlags);
-static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
+static void make_ruledef(StringInfo buf, TupleTableSlot *ruleslot, int prettyFlags);
+static void make_viewdef(StringInfo buf, TupleTableSlot *ruleslot,
int prettyFlags, int wrapColumn);
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
TupleDesc resultDesc,
@@ -515,8 +514,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
Datum args[1];
char nulls[1];
int spirc;
- HeapTuple ruletup;
- TupleDesc rulettc;
+ TupleTableSlot *ruleslot;
StringInfoData buf;
/*
@@ -568,9 +566,8 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
/*
* Get the rule's definition and put it into executor's memory
*/
- ruletup = SPI_tuptable->vals[0];
- rulettc = SPI_tuptable->tupdesc;
- make_ruledef(&buf, ruletup, rulettc, prettyFlags);
+ ruleslot = SPI_tuptable->vals[0];
+ make_ruledef(&buf, ruleslot, prettyFlags);
}
/*
@@ -708,8 +705,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
Datum args[2];
char nulls[2];
int spirc;
- HeapTuple ruletup;
- TupleDesc rulettc;
+ TupleTableSlot *ruleslot;
StringInfoData buf;
/*
@@ -764,9 +760,8 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
/*
* Get the rule's definition and put it into executor's memory
*/
- ruletup = SPI_tuptable->vals[0];
- rulettc = SPI_tuptable->tupdesc;
- make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
+ ruleslot = SPI_tuptable->vals[0];
+ make_viewdef(&buf, ruleslot, prettyFlags, wrapColumn);
}
/*
@@ -4580,8 +4575,7 @@ pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
* ----------
*/
static void
-make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
- int prettyFlags)
+make_ruledef(StringInfo buf, TupleTableSlot *ruleslot, int prettyFlags)
{
char *rulename;
char ev_type;
@@ -4593,36 +4587,37 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int fno;
Datum dat;
bool isnull;
+ TupleDesc rulettc = ruleslot->tts_tupleDescriptor;
/*
* Get the attribute values from the rules tuple
*/
fno = SPI_fnumber(rulettc, "rulename");
- dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ dat = SPI_getbinval(ruleslot, fno, &isnull);
Assert(!isnull);
rulename = NameStr(*(DatumGetName(dat)));
fno = SPI_fnumber(rulettc, "ev_type");
- dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ dat = SPI_getbinval(ruleslot, fno, &isnull);
Assert(!isnull);
ev_type = DatumGetChar(dat);
fno = SPI_fnumber(rulettc, "ev_class");
- dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ dat = SPI_getbinval(ruleslot, fno, &isnull);
Assert(!isnull);
ev_class = DatumGetObjectId(dat);
fno = SPI_fnumber(rulettc, "is_instead");
- dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ dat = SPI_getbinval(ruleslot, fno, &isnull);
Assert(!isnull);
is_instead = DatumGetBool(dat);
/* these could be nulls */
fno = SPI_fnumber(rulettc, "ev_qual");
- ev_qual = SPI_getvalue(ruletup, rulettc, fno);
+ ev_qual = SPI_getvalue(ruleslot, fno);
fno = SPI_fnumber(rulettc, "ev_action");
- ev_action = SPI_getvalue(ruletup, rulettc, fno);
+ ev_action = SPI_getvalue(ruleslot, fno);
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
@@ -4762,7 +4757,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
* ----------
*/
static void
-make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
+make_viewdef(StringInfo buf, TupleTableSlot *ruleslot,
int prettyFlags, int wrapColumn)
{
Query *query;
@@ -4775,24 +4770,25 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
Relation ev_relation;
int fno;
bool isnull;
+ TupleDesc rulettc = ruleslot->tts_tupleDescriptor;
/*
* Get the attribute values from the rules tuple
*/
fno = SPI_fnumber(rulettc, "ev_type");
- ev_type = (char) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ ev_type = (char) SPI_getbinval(ruleslot, fno, &isnull);
fno = SPI_fnumber(rulettc, "ev_class");
- ev_class = (Oid) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ ev_class = (Oid) SPI_getbinval(ruleslot, fno, &isnull);
fno = SPI_fnumber(rulettc, "is_instead");
- is_instead = (bool) SPI_getbinval(ruletup, rulettc, fno, &isnull);
+ is_instead = (bool) SPI_getbinval(ruleslot, fno, &isnull);
fno = SPI_fnumber(rulettc, "ev_qual");
- ev_qual = SPI_getvalue(ruletup, rulettc, fno);
+ ev_qual = SPI_getvalue(ruleslot, fno);
fno = SPI_fnumber(rulettc, "ev_action");
- ev_action = SPI_getvalue(ruletup, rulettc, fno);
+ ev_action = SPI_getvalue(ruleslot, fno);
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c
index 50ea6d9..7709acd 100644
--- a/src/backend/utils/adt/trigfuncs.c
+++ b/src/backend/utils/adt/trigfuncs.c
@@ -60,8 +60,8 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS)
errmsg("suppress_redundant_updates_trigger: must be called for each row")));
/* get tuple data, set default result */
- rettuple = newtuple = trigdata->tg_newtuple;
- oldtuple = trigdata->tg_trigtuple;
+ rettuple = newtuple = trigdata->tg_newslot->tts_tuple;
+ oldtuple = trigdata->tg_trigslot->tts_tuple;
newheader = newtuple->t_data;
oldheader = oldtuple->t_data;
diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c
index 1bd3dea..e90b702 100644
--- a/src/backend/utils/adt/tsquery_rewrite.c
+++ b/src/backend/utils/adt/tsquery_rewrite.c
@@ -326,13 +326,13 @@ tsquery_rewrite_query(PG_FUNCTION_ARGS)
for (i = 0; i < SPI_processed && tree; i++)
{
- Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+ Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], 1, &isnull);
Datum sdata;
if (isnull)
continue;
- sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
+ sdata = SPI_getbinval(SPI_tuptable->vals[i], 2, &isnull);
if (!isnull)
{
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 2d7407c..448a2af 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -2357,7 +2357,7 @@ ts_stat_sql(MemoryContext persistentContext, text *txt, text *ws)
for (i = 0; i < SPI_processed; i++)
{
- Datum data = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+ Datum data = SPI_getbinval(SPI_tuptable->vals[i], 1, &isnull);
if (!isnull)
stat = ts_accum(persistentContext, stat, data);
@@ -2456,7 +2456,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
TriggerData *trigdata;
Trigger *trigger;
Relation rel;
- HeapTuple rettuple = NULL;
+ TupleTableSlot *retslot = NULL;
int tsvector_attr_num,
i;
ParsedText prs;
@@ -2476,9 +2476,9 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- rettuple = trigdata->tg_trigtuple;
+ retslot = trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- rettuple = trigdata->tg_newtuple;
+ retslot = trigdata->tg_newslot;
else
elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");
@@ -2521,7 +2521,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
errmsg("column \"%s\" is not of regconfig type",
trigger->tgargs[1])));
- datum = SPI_getbinval(rettuple, rel->rd_att, config_attr_num, &isnull);
+ datum = SPI_getbinval(retslot, config_attr_num, &isnull);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -2566,7 +2566,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
errmsg("column \"%s\" is not of a character type",
trigger->tgargs[i])));
- datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
+ datum = SPI_getbinval(retslot, numattr, &isnull);
if (isnull)
continue;
@@ -2583,9 +2583,11 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
{
datum = PointerGetDatum(make_tsvector(&prs));
isnull = false;
- rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
- 1, &tsvector_attr_num,
- &datum, &isnull);
+
+ Assert(retslot->tts_tupleDescriptor != rel->rd_att);
+ retslot = heap_modify_slot_by_cols(retslot,
+ 1, &tsvector_attr_num,
+ &datum, &isnull);
pfree(DatumGetPointer(datum));
}
else
@@ -2596,11 +2598,13 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
out->size = 0;
datum = PointerGetDatum(out);
isnull = false;
- rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
+
+ Assert(retslot->tts_tupleDescriptor != rel->rd_att);
+ retslot = heap_modify_slot_by_cols(retslot,
1, &tsvector_attr_num,
&datum, &isnull);
pfree(prs.words);
}
- return PointerGetDatum(rettuple);
+ return PointerGetDatum(retslot->tts_tuple);
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 323614c..e0deec1 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -2331,7 +2331,6 @@ query_to_oid_list(const char *query)
bool isnull;
oid = SPI_getbinval(SPI_tuptable->vals[i],
- SPI_tuptable->tupdesc,
1,
&isnull);
if (!isnull)
@@ -3638,7 +3637,6 @@ SPI_sql_row_to_xmlelement(uint64 rownum, StringInfo result, char *tablename,
colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i),
true, false);
colval = SPI_getbinval(SPI_tuptable->vals[rownum],
- SPI_tuptable->tupdesc,
i,
&isnull);
if (isnull)
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68..cb3f9f0 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -68,6 +68,7 @@ typedef struct HeapTupleData
} HeapTupleData;
typedef HeapTupleData *HeapTuple;
+typedef void* StorageTuple;
#define HEAPTUPLESIZE MAXALIGN(sizeof(HeapTupleData))
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 36c1134..7bf6e41 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -32,11 +32,9 @@ typedef struct TriggerData
NodeTag type;
TriggerEvent tg_event;
Relation tg_relation;
- HeapTuple tg_trigtuple;
- HeapTuple tg_newtuple;
+ TupleTableSlot* tg_trigslot;
+ TupleTableSlot* tg_newslot;
Trigger *tg_trigger;
- Buffer tg_trigtuplebuf;
- Buffer tg_newtuplebuf;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
} TriggerData;
@@ -188,7 +186,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
@@ -203,15 +201,15 @@ extern bool ExecBRDeleteTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple);
+ TupleTableSlot *trigslot);
extern void ExecARDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
+ TupleTableSlot *slot,
TransitionCaptureState *transition_capture);
extern bool ExecIRDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple);
+ TupleTableSlot *slot);
extern void ExecBSUpdateTriggers(EState *estate,
ResultRelInfo *relinfo);
extern void ExecASUpdateTriggers(EState *estate,
@@ -221,18 +219,18 @@ extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
+ TupleTableSlot *trigslot,
TupleTableSlot *slot);
extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
- HeapTuple newtuple,
+ TupleTableSlot *oldslot,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *oldslot,
TupleTableSlot *slot);
extern void ExecBSTruncateTriggers(EState *estate,
ResultRelInfo *relinfo);
@@ -254,9 +252,9 @@ extern bool AfterTriggerPendingOnRel(Oid relid);
* in utils/adt/ri_triggers.c
*/
extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
- HeapTuple old_row, HeapTuple new_row);
+ TupleTableSlot *oldslot, TupleTableSlot *newslot);
extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
- HeapTuple old_row, HeapTuple new_row);
+ TupleTableSlot *oldslot, TupleTableSlot *newslot);
extern bool RI_Initial_Check(Trigger *trigger,
Relation fk_rel, Relation pk_rel);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e..ca93f89 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ TupleTableSlot **vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -121,10 +121,12 @@ extern HeapTuple SPI_copytuple(HeapTuple tuple);
extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
+extern TupleTableSlot* SPI_modifyslot(TupleTableSlot *slot, int natts, int *attnum,
+ Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
-extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber);
-extern Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull);
+extern char *SPI_getvalue(TupleTableSlot *slot, int fnumber);
+extern Datum SPI_getbinval(TupleTableSlot *slot, int fnumber, bool *isnull);
extern char *SPI_gettype(TupleDesc tupdesc, int fnumber);
extern Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber);
extern char *SPI_getrelname(Relation rel);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce..13706aa 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -117,6 +117,7 @@ typedef struct TupleTableSlot
bool tts_shouldFree; /* should pfree tts_tuple? */
bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
bool tts_slow; /* saved state for slot_deform_tuple */
+ ItemPointerData tts_tid;
HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
@@ -152,6 +153,11 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
TupleTableSlot *slot,
bool shouldFree);
+TupleTableSlot* heap_modify_slot_by_cols(TupleTableSlot *slot,
+ int nCols,
+ int *replCols,
+ Datum *replValues,
+ bool *replIsnull);
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5a45e3e..a760a24 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1584,7 +1584,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
event = "INSERT";
if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
hv_store_string(hv, "new",
- plperl_hash_from_tuple(tdata->tg_trigtuple,
+ plperl_hash_from_tuple(tdata->tg_trigslot,
tupdesc));
}
else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
@@ -1592,7 +1592,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
event = "DELETE";
if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
hv_store_string(hv, "old",
- plperl_hash_from_tuple(tdata->tg_trigtuple,
+ plperl_hash_from_tuple(tdata->tg_trigslot,
tupdesc));
}
else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
@@ -1601,10 +1601,10 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo)
if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
{
hv_store_string(hv, "old",
- plperl_hash_from_tuple(tdata->tg_trigtuple,
+ plperl_hash_from_tuple(tdata->tg_trigslot,
tupdesc));
hv_store_string(hv, "new",
- plperl_hash_from_tuple(tdata->tg_newtuple,
+ plperl_hash_from_tuple(tdata->tg_newslot,
tupdesc));
}
}
@@ -2486,13 +2486,13 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
TriggerData *trigdata = ((TriggerData *) fcinfo->context);
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- retval = (Datum) trigdata->tg_trigtuple;
+ retval = (Datum) trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- retval = (Datum) trigdata->tg_newtuple;
+ retval = (Datum) trigdata->tg_newslot;
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
- retval = (Datum) trigdata->tg_trigtuple;
+ retval = (Datum) trigdata->tg_trigslot;
else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
- retval = (Datum) trigdata->tg_trigtuple;
+ retval = (Datum) trigdata->tg_trigslot;
else
retval = (Datum) 0; /* can this happen? */
}
@@ -2511,10 +2511,10 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
trv = plperl_modify_tuple(hvTD, trigdata,
- trigdata->tg_trigtuple);
+ trigdata->tg_trigslot);
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
trv = plperl_modify_tuple(hvTD, trigdata,
- trigdata->tg_newtuple);
+ trigdata->tg_newslot);
else
{
ereport(WARNING,
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 662b3c9..019404d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1949,7 +1949,7 @@ plpgsql_build_record(const char *refname, int lineno, bool add2namespace)
rec->dtype = PLPGSQL_DTYPE_REC;
rec->refname = pstrdup(refname);
rec->lineno = lineno;
- rec->tup = NULL;
+ rec->slot = NULL;
rec->tupdesc = NULL;
rec->freetup = false;
rec->freetupdesc = false;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index c98492b..6f50937 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -275,7 +275,7 @@ static void plpgsql_param_fetch(ParamListInfo params, int paramid);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
PLpgSQL_row *row,
- HeapTuple tup, TupleDesc tupdesc);
+ TupleTableSlot *slot, TupleDesc tupdesc);
static HeapTuple make_tuple_from_row(PLpgSQL_execstate *estate,
PLpgSQL_row *row,
TupleDesc tupdesc);
@@ -612,7 +612,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
* trigger execution.
* ----------
*/
-HeapTuple
+TupleTableSlot *
plpgsql_exec_trigger(PLpgSQL_function *func,
TriggerData *trigdata)
{
@@ -668,23 +668,23 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
/*
* Per-statement triggers don't use OLD/NEW variables
*/
- rec_new->tup = NULL;
- rec_old->tup = NULL;
+ rec_new->slot = NULL;
+ rec_old->slot = NULL;
}
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
{
- rec_new->tup = trigdata->tg_trigtuple;
- rec_old->tup = NULL;
+ rec_new->slot = trigdata->tg_trigslot;
+ rec_old->slot = NULL;
}
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
- rec_new->tup = trigdata->tg_newtuple;
- rec_old->tup = trigdata->tg_trigtuple;
+ rec_new->slot = trigdata->tg_newslot;
+ rec_old->slot = trigdata->tg_trigslot;
}
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
{
- rec_new->tup = NULL;
- rec_old->tup = trigdata->tg_trigtuple;
+ rec_new->slot = NULL;
+ rec_old->slot = trigdata->tg_trigslot;
}
else
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
@@ -1054,7 +1054,7 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
memcpy(new, datum, sizeof(PLpgSQL_rec));
/* should be preset to null/non-freeable */
- Assert(new->tup == NULL);
+ Assert(new->slot == NULL);
Assert(new->tupdesc == NULL);
Assert(!new->freetup);
Assert(!new->freetupdesc);
@@ -1240,7 +1240,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
if (rec->freetup)
{
- heap_freetuple(rec->tup);
+ ExecDropSingleTupleTableSlot(rec->slot);
rec->freetup = false;
}
if (rec->freetupdesc)
@@ -1248,7 +1248,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
FreeTupleDesc(rec->tupdesc);
rec->freetupdesc = false;
}
- rec->tup = NULL;
+ rec->slot = NULL;
rec->tupdesc = NULL;
}
break;
@@ -2695,11 +2695,11 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
int32 rettypmod;
- if (HeapTupleIsValid(rec->tup))
+ if (HeapTupleIsValid(rec->slot))
{
if (estate->retistuple)
{
- estate->retval = PointerGetDatum(rec->tup);
+ estate->retval = PointerGetDatum(rec->slot->tts_tuple);
estate->rettupdesc = rec->tupdesc;
estate->retisnull = false;
}
@@ -2872,7 +2872,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
TupleConversionMap *tupmap;
- if (!HeapTupleIsValid(rec->tup))
+ if (!HeapTupleIsValid(rec->slot))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("record \"%s\" is not assigned yet",
@@ -2885,7 +2885,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
tupmap = convert_tuples_by_position(rec->tupdesc,
tupdesc,
gettext_noop("wrong record type supplied in RETURN NEXT"));
- tuple = rec->tup;
+ tuple = rec->slot->tts_tuple;
if (tupmap)
tuple = do_convert_tuple(tuple, tupmap);
tuplestore_puttuple(estate->tuple_store, tuple);
@@ -3058,10 +3058,11 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
for (i = 0; i < SPI_processed; i++)
{
- HeapTuple tuple = SPI_tuptable->vals[i];
+ TupleTableSlot *slot = SPI_tuptable->vals[i];
+ HeapTuple tuple;
if (tupmap)
- tuple = do_convert_tuple(tuple, tupmap);
+ tuple = do_convert_tuple(slot->tts_tuple, tupmap);
tuplestore_puttuple(estate->tuple_store, tuple);
if (tupmap)
heap_freetuple(tuple);
@@ -4552,7 +4553,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
PLpgSQL_rec *rec;
int fno;
- HeapTuple newtup;
int colnums[1];
Datum values[1];
bool nulls[1];
@@ -4566,7 +4566,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* that because records don't have any predefined field
* structure.
*/
- if (!HeapTupleIsValid(rec->tup))
+ if (!HeapTupleIsValid(rec->slot))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("record \"%s\" is not assigned yet",
@@ -4600,15 +4600,9 @@ exec_assign_value(PLpgSQL_execstate *estate,
atttypmod);
nulls[0] = isNull;
- newtup = heap_modify_tuple_by_cols(rec->tup, rec->tupdesc,
+ rec->slot = heap_modify_slot_by_cols(rec->slot,
1, colnums, values, nulls);
-
- if (rec->freetup)
- heap_freetuple(rec->tup);
-
- rec->tup = newtup;
- rec->freetup = true;
-
+ rec->freetup = false;
break;
}
@@ -4874,7 +4868,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
- if (!HeapTupleIsValid(rec->tup))
+ if (!HeapTupleIsValid(rec->slot))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("record \"%s\" is not assigned yet",
@@ -4887,7 +4881,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
*typeid = rec->tupdesc->tdtypeid;
*typetypmod = rec->tupdesc->tdtypmod;
- *value = heap_copy_tuple_as_datum(rec->tup, rec->tupdesc);
+ *value = heap_copy_tuple_as_datum(rec->slot->tts_tuple, rec->tupdesc);
*isnull = false;
MemoryContextSwitchTo(oldcontext);
break;
@@ -4900,7 +4894,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
int fno;
rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
- if (!HeapTupleIsValid(rec->tup))
+ if (!HeapTupleIsValid(rec->slot))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("record \"%s\" is not assigned yet",
@@ -4917,7 +4911,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
*typetypmod = rec->tupdesc->attrs[fno - 1]->atttypmod;
else
*typetypmod = -1;
- *value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+ *value = SPI_getbinval(rec->slot, fno, isnull);
break;
}
@@ -5236,8 +5230,7 @@ exec_eval_expr(PLpgSQL_execstate *estate,
/*
* Return the single result Datum.
*/
- return SPI_getbinval(estate->eval_tuptable->vals[0],
- estate->eval_tuptable->tupdesc, 1, isNull);
+ return SPI_getbinval(estate->eval_tuptable->vals[0], 1, isNull);
}
@@ -5936,7 +5929,7 @@ static void
exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
PLpgSQL_row *row,
- HeapTuple tup, TupleDesc tupdesc)
+ TupleTableSlot *slot, TupleDesc tupdesc)
{
/*
* Record is simple - just copy the tuple and its descriptor into the
@@ -5944,30 +5937,13 @@ exec_move_row(PLpgSQL_execstate *estate,
*/
if (rec != NULL)
{
- /*
- * Copy input first, just in case it is pointing at variable's value
- */
- if (HeapTupleIsValid(tup))
- tup = heap_copytuple(tup);
- else if (tupdesc)
- {
- /* If we have a tupdesc but no data, form an all-nulls tuple */
- bool *nulls;
-
- nulls = (bool *)
- eval_mcontext_alloc(estate, tupdesc->natts * sizeof(bool));
- memset(nulls, true, tupdesc->natts * sizeof(bool));
-
- tup = heap_form_tuple(tupdesc, NULL, nulls);
- }
-
if (tupdesc)
tupdesc = CreateTupleDescCopy(tupdesc);
/* Free the old value ... */
if (rec->freetup)
{
- heap_freetuple(rec->tup);
+ ExecDropSingleTupleTableSlot(rec->slot);
rec->freetup = false;
}
if (rec->freetupdesc)
@@ -5977,13 +5953,13 @@ exec_move_row(PLpgSQL_execstate *estate,
}
/* ... and install the new */
- if (HeapTupleIsValid(tup))
+ if (HeapTupleIsValid(slot))
{
- rec->tup = tup;
+ ExecCopySlot(rec->slot, slot);
rec->freetup = true;
}
else
- rec->tup = NULL;
+ rec->slot = NULL;
if (tupdesc)
{
@@ -6019,8 +5995,8 @@ exec_move_row(PLpgSQL_execstate *estate,
int fnum;
int anum;
- if (HeapTupleIsValid(tup))
- t_natts = HeapTupleHeaderGetNatts(tup->t_data);
+ if (HeapTupleIsValid(slot))
+ t_natts = HeapTupleHeaderGetNatts(slot->tts_tuple->t_data);
else
t_natts = 0;
@@ -6044,7 +6020,7 @@ exec_move_row(PLpgSQL_execstate *estate,
if (anum < td_natts)
{
if (anum < t_natts)
- value = SPI_getbinval(tup, tupdesc, anum + 1, &isnull);
+ value = SPI_getbinval(slot, anum + 1, &isnull);
else
{
value = (Datum) 0;
@@ -6188,6 +6164,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tmptup;
+ TupleTableSlot *slot;
/* Extract rowtype info and find a tupdesc */
tupType = HeapTupleHeaderGetTypeId(td);
@@ -6200,8 +6177,11 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = td;
+ slot = MakeSingleTupleTableSlot(tupdesc);
+ slot->tts_tuple = &tmptup;
+
/* Do the move */
- exec_move_row(estate, rec, row, &tmptup, tupdesc);
+ exec_move_row(estate, rec, row, slot, tupdesc);
/* Release tupdesc usage count */
ReleaseTupleDesc(tupdesc);
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 2b19948..fc46206 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -304,7 +304,7 @@ typedef struct PLpgSQL_rec
char *refname;
int lineno;
- HeapTuple tup;
+ TupleTableSlot *slot;
TupleDesc tupdesc;
bool freetup;
bool freetupdesc;
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index c6938d0..63cdc31 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -790,36 +790,36 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
pltevent = PyString_FromString("INSERT");
PyDict_SetItemString(pltdata, "old", Py_None);
- pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+ pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
tdata->tg_relation->rd_att);
PyDict_SetItemString(pltdata, "new", pytnew);
Py_DECREF(pytnew);
- *rv = tdata->tg_trigtuple;
+ *rv = tdata->tg_trigslot;
}
else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
{
pltevent = PyString_FromString("DELETE");
PyDict_SetItemString(pltdata, "new", Py_None);
- pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+ pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
tdata->tg_relation->rd_att);
PyDict_SetItemString(pltdata, "old", pytold);
Py_DECREF(pytold);
- *rv = tdata->tg_trigtuple;
+ *rv = tdata->tg_trigslot;
}
else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
{
pltevent = PyString_FromString("UPDATE");
- pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple,
+ pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newslot,
tdata->tg_relation->rd_att);
PyDict_SetItemString(pltdata, "new", pytnew);
Py_DECREF(pytnew);
- pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+ pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigslot,
tdata->tg_relation->rd_att);
PyDict_SetItemString(pltdata, "old", pytold);
Py_DECREF(pytold);
- *rv = tdata->tg_newtuple;
+ *rv = tdata->tg_newslot;
}
else
{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index ed494e1..c335eef 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1134,7 +1134,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
Tcl_NewStringObj("ROW", -1));
/* Build the data list for the trigtuple */
- tcl_trigtup = pltcl_build_tuple_argument(trigdata->tg_trigtuple,
+ tcl_trigtup = pltcl_build_tuple_argument(trigdata->tg_trigslot,
tupdesc);
/*
@@ -1149,7 +1149,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
- rettup = trigdata->tg_trigtuple;
+ rettup = trigdata->tg_trigslot;
}
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
{
@@ -1159,20 +1159,20 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
- rettup = trigdata->tg_trigtuple;
+ rettup = trigdata->tg_trigslot;
}
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
Tcl_ListObjAppendElement(NULL, tcl_cmd,
Tcl_NewStringObj("UPDATE", -1));
- tcl_newtup = pltcl_build_tuple_argument(trigdata->tg_newtuple,
+ tcl_newtup = pltcl_build_tuple_argument(trigdata->tg_newslot,
tupdesc);
Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_newtup);
Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
- rettup = trigdata->tg_newtuple;
+ rettup = trigdata->tg_newslot;
}
else
elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 12c8cd5..cb61dcf 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -126,7 +126,6 @@ initialize_worker_spi(worktable *table)
elog(FATAL, "not a singleton result");
ntup = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
- SPI_tuptable->tupdesc,
1, &isnull));
if (isnull)
elog(FATAL, "null result");
@@ -281,7 +280,6 @@ worker_spi_main(Datum main_arg)
int32 val;
val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
- SPI_tuptable->tupdesc,
1, &isnull));
if (!isnull)
elog(LOG, "%s: count in %s.%s is now %d",
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index b73bcce..5e0a941 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -347,7 +347,7 @@ funny_dup17(PG_FUNCTION_ARGS)
bool *recursion;
Relation rel;
TupleDesc tupdesc;
- HeapTuple tuple;
+ TupleTableSlot *slot;
char *query,
*fieldval,
*fieldtype;
@@ -359,7 +359,7 @@ funny_dup17(PG_FUNCTION_ARGS)
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "funny_dup17: not fired by trigger manager");
- tuple = trigdata->tg_trigtuple;
+ slot = trigdata->tg_trigslot;
rel = trigdata->tg_relation;
tupdesc = rel->rd_att;
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
@@ -387,17 +387,17 @@ funny_dup17(PG_FUNCTION_ARGS)
if (*level == 17)
{
*recursion = false;
- return PointerGetDatum(tuple);
+ return PointerGetDatum(slot->tts_tuple);
}
if (!(*recursion))
- return PointerGetDatum(tuple);
+ return PointerGetDatum(slot->tts_tuple);
(*level)++;
SPI_connect();
- fieldval = SPI_getvalue(tuple, tupdesc, 1);
+ fieldval = SPI_getvalue(slot, 1);
fieldtype = SPI_gettype(tupdesc, 1);
query = (char *) palloc(100 + NAMEDATALEN * 3 +
@@ -426,11 +426,9 @@ funny_dup17(PG_FUNCTION_ARGS)
if (SPI_processed > 0)
{
selected = DatumGetInt32(DirectFunctionCall1(int4in,
- CStringGetDatum(SPI_getvalue(
- SPI_tuptable->vals[0],
- SPI_tuptable->tupdesc,
- 1
- ))));
+ CStringGetDatum(SPI_getvalue(
+ SPI_tuptable->vals[0],
+ 1))));
}
elog(DEBUG4, "funny_dup17 (fired %s) on level %3d: " UINT64_FORMAT "/%d tuples inserted/selected",
@@ -443,7 +441,7 @@ funny_dup17(PG_FUNCTION_ARGS)
if (*level == 0)
*xid = InvalidTransactionId;
- return PointerGetDatum(tuple);
+ return PointerGetDatum(slot->tts_tuple);
}
#define TTDUMMY_INFINITY 999999
@@ -468,8 +466,8 @@ ttdummy(PG_FUNCTION_ARGS)
char *cnulls; /* column nulls */
char *relname; /* triggered relation name */
Relation rel; /* triggered relation */
- HeapTuple trigtuple;
- HeapTuple newtuple = NULL;
+ TupleTableSlot *trigslot;
+ TupleTableSlot *newslot = NULL;
HeapTuple rettuple;
TupleDesc tupdesc; /* tuple description */
int natts; /* # of attributes */
@@ -486,9 +484,9 @@ ttdummy(PG_FUNCTION_ARGS)
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
elog(ERROR, "ttdummy: cannot process INSERT event");
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- newtuple = trigdata->tg_newtuple;
+ newslot = trigdata->tg_newslot;
- trigtuple = trigdata->tg_trigtuple;
+ trigslot = trigdata->tg_trigslot;
rel = trigdata->tg_relation;
relname = SPI_getrelname(rel);
@@ -497,7 +495,8 @@ ttdummy(PG_FUNCTION_ARGS)
if (ttoff) /* OFF - nothing to do */
{
pfree(relname);
- return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
+ return PointerGetDatum((newslot != NULL) ?
+ newslot->tts_tuple : trigslot->tts_tuple);
}
trigger = trigdata->tg_trigger;
@@ -521,20 +520,20 @@ ttdummy(PG_FUNCTION_ARGS)
relname, args[i]);
}
- oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull);
+ oldon = SPI_getbinval(trigslot, attnum[0], &isnull);
if (isnull)
elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
- oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull);
+ oldoff = SPI_getbinval(trigslot, attnum[1], &isnull);
if (isnull)
elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
- if (newtuple != NULL) /* UPDATE */
+ if (newslot != NULL) /* UPDATE */
{
- newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull);
+ newon = SPI_getbinval(newslot, attnum[0], &isnull);
if (isnull)
elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[0]);
- newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull);
+ newoff = SPI_getbinval(newslot, attnum[1], &isnull);
if (isnull)
elog(ERROR, "ttdummy (%s): %s must be NOT NULL", relname, args[1]);
@@ -569,13 +568,13 @@ ttdummy(PG_FUNCTION_ARGS)
cnulls = (char *) palloc(natts * sizeof(char));
for (i = 0; i < natts; i++)
{
- cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple,
- tupdesc, i + 1, &isnull);
+ cvals[i] = SPI_getbinval((newslot != NULL) ? newslot : trigslot,
+ i + 1, &isnull);
cnulls[i] = (isnull) ? 'n' : ' ';
}
/* change date column(s) */
- if (newtuple) /* UPDATE */
+ if (newslot) /* UPDATE */
{
cvals[attnum[0] - 1] = newoff; /* start_date eq current date */
cnulls[attnum[0] - 1] = ' ';
@@ -628,10 +627,10 @@ ttdummy(PG_FUNCTION_ARGS)
elog(ERROR, "ttdummy (%s): SPI_execp returned %d", relname, ret);
/* Tuple to return to upper Executor ... */
- if (newtuple) /* UPDATE */
- rettuple = SPI_modifytuple(rel, trigtuple, 1, &(attnum[1]), &newoff, NULL);
- else /* DELETE */
- rettuple = trigtuple;
+ if (newslot) /* UPDATE */
+ trigslot = SPI_modifyslot(trigslot, 1, &(attnum[1]), &newoff, NULL);
+
+ rettuple = trigslot->tts_tuple;
SPI_finish(); /* don't forget say Bye to SPI mgr */
--
1.8.3.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-07-15 02:30 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-07-15 02:30 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Jul 14, 2017 at 8:35 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
> To replace tuple with slot, I took trigger and SPI calls as the first step
> in modifying
> from tuple to slot, Here I attached a WIP patch. The notable changes are,
>
> 1. Replace most of the HeapTuple with Slot in SPI interface functions.
> 2. In SPITupleTable, Instead of HeapTuple, it is changed to TupleTableSlot.
> But this change may not be a proper approach, because a duplicate copy of
> TupleTableSlot is generated and stored.
> 3. Changed all trigger interfaces to accept TupleTableSlot Instead of
> HeapTuple.
> 4. ItemPointerData is added as a member to the TupleTableSlot structure.
> 5. Modified the ExecInsert and others to work directly on TupleTableSlot
> instead
> of tuple(not completely).
What problem are you trying to solve with these changes? I'm not
saying that it's a bad idea, but I think you should spell out the
motivation a little more explicitly.
I think performance is likely to be a key concern here. Maybe these
interfaces are Just Better with TupleTableSlots and the patch is a win
regardless of pluggable storage -- but if the reverse turns out to be
true, and this slows things down in cases that anyone cares about, I
think that's going to be a problem.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-07-19 06:03 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-07-19 06:03 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Jul 15, 2017 at 12:30 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Jul 14, 2017 at 8:35 AM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> > To replace tuple with slot, I took trigger and SPI calls as the first
> step
> > in modifying
> > from tuple to slot, Here I attached a WIP patch. The notable changes are,
> >
> > 1. Replace most of the HeapTuple with Slot in SPI interface functions.
> > 2. In SPITupleTable, Instead of HeapTuple, it is changed to
> TupleTableSlot.
> > But this change may not be a proper approach, because a duplicate copy of
> > TupleTableSlot is generated and stored.
> > 3. Changed all trigger interfaces to accept TupleTableSlot Instead of
> > HeapTuple.
> > 4. ItemPointerData is added as a member to the TupleTableSlot structure.
> > 5. Modified the ExecInsert and others to work directly on TupleTableSlot
> > instead
> > of tuple(not completely).
>
> What problem are you trying to solve with these changes? I'm not
> saying that it's a bad idea, but I think you should spell out the
> motivation a little more explicitly.
>
Sorry for not providing complete details. I am trying these experiments
to find out the best way to return the tuple from Storage methods by
designing a proper API.
The changes that I am doing are to reduce the dependency on the
HeapTuple format with value/nulls array. So if there is no dependency
on the HeapTuple format in the upper layers of the PostgreSQL storage,
then directly we can define the StorageAPI to return the value/nulls array
instead of one big chunck of tuple data like HeapTuple or StorageTuple(void
*).
I am finding out that eliminating the HeapTuple usage in the upper layers
needs some major changes, How about not changing anything in the upper
layers of storage currently and just support the pluggable tuple with one of
the following approach for first version?
1. Design an API that returns values/nulls array and convert that into a
HeapTuple whenever it is required in the upper layers. All the existing
heap form/deform tuples are used for every tuple with some adjustments.
2. Design an API that returns StorageTuple(void *) with first member
represents the TYPE of the storage, so that corresponding registered
function calls can be called to deform/form the tuple whenever there is
a need of tuple.
3. Design an API that returns StorageTuple(void *) but the necessary
format information of that tuple can be get from the tupledesc. wherever
the tuple is present, there exists a tupledesc in most of the cases. How
about adding some kind of information in tupledesc to find out the tuple
format and call the necessary functions
4. Design an API to return always the StorageTuple and it converts to
HeapTuple with a function hook if it gets registered (for heap storages
this is not required to register the hook, because it is already a HeapTuple
format). This function hook should be placed in the heap form/deform
functions.
Any other better ideas for a first version.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-07-23 06:10 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-07-23 06:10 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Jul 19, 2017 at 11:33 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
>
> On Sat, Jul 15, 2017 at 12:30 PM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>
> I am finding out that eliminating the HeapTuple usage in the upper layers
> needs some major changes, How about not changing anything in the upper
> layers of storage currently and just support the pluggable tuple with one of
> the following approach for first version?
>
It is not very clear to me how any of the below alternatives are
better or worse as compare to the approach you are already working.
Do you mean to say that we will get away without changing all the
places which take HeapTuple as input or return it as output with some
of the below approaches?
> 1. Design an API that returns values/nulls array and convert that into a
> HeapTuple whenever it is required in the upper layers. All the existing
> heap form/deform tuples are used for every tuple with some adjustments.
>
So, this would have the additional cost of form/deform. Also, how
would it have lesser changes as compare to what you have described
earlier?
> 2. Design an API that returns StorageTuple(void *) with first member
> represents the TYPE of the storage, so that corresponding registered
> function calls can be called to deform/form the tuple whenever there is
> a need of tuple.
>
Do you intend to say that we store such information in disk tuple or
only in the in-memory version of same? Also, what makes you think
that we would need hooks only for form and deform? Right now, in many
cases tuple will directly point to disk page and we deal with it by
retaining the pin on the corresponding buffer, what if some kinds of
tuple don't follow that rule? For ex. to support in-place updates, we
might always need a separate copy of tuple rather than the one
pointing to disk page.
> 3. Design an API that returns StorageTuple(void *) but the necessary
> format information of that tuple can be get from the tupledesc. wherever
> the tuple is present, there exists a tupledesc in most of the cases. How
> about adding some kind of information in tupledesc to find out the tuple
> format and call the necessary functions
>
> 4. Design an API to return always the StorageTuple and it converts to
> HeapTuple with a function hook if it gets registered (for heap storages
> this is not required to register the hook, because it is already a HeapTuple
> format). This function hook should be placed in the heap form/deform
> functions.
>
I think some more information is required to comment on any of the
approaches or suggest a new one. You might want to try by quoting
some specific examples from code so that it is easy to understand what
your proposal will change in that case. One idea could be that we
start with some interfaces like structures TupleTableSlot, EState,
HeapScanDescData, IndexScanDescData, etc. and interfaces like
heap_insert, heap_update, heap_lock_tuple,
SnapshotSatisfiesFunc, EvalPlanQualFetch, etc. Now, it is quite
possible that we don't want to change some of these interfaces, but it
can help to see how such a usage can be replaced with new kind of
Tuple structure.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-08-01 08:26 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-08-01 08:26 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sun, Jul 23, 2017 at 4:10 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Wed, Jul 19, 2017 at 11:33 AM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> >
> > I am finding out that eliminating the HeapTuple usage in the upper layers
> > needs some major changes, How about not changing anything in the upper
> > layers of storage currently and just support the pluggable tuple with
> one of
> > the following approach for first version?
> >
>
> It is not very clear to me how any of the below alternatives are
> better or worse as compare to the approach you are already working.
> Do you mean to say that we will get away without changing all the
> places which take HeapTuple as input or return it as output with some
> of the below approaches?
>
Yes, With the following approaches, my intention is to reduce the changing
of HeapTuple everywhere to support the Pluggable Storage API with minimal
changes and later improvise further. But until unless we change the
HeapTuple
everywhere properly, may be we can't see the benefits of pluggable storages.
> > 1. Design an API that returns values/nulls array and convert that into a
> > HeapTuple whenever it is required in the upper layers. All the existing
> > heap form/deform tuples are used for every tuple with some adjustments.
> >
>
> So, this would have the additional cost of form/deform. Also, how
> would it have lesser changes as compare to what you have described
> earlier?
>
Yes, It have the additional cost of form/deform. It is the same approach
that
is described earlier. But I have an intention of modifying everywhere the
HeapTuple is accessed. But with the other prototype changes of removing
HeapTuple usage from triggers, I realized that it needs some clear design
to proceed further, instead of combining those changes with pluggable
Storage API.
- heap_getnext function is kept as it as and it is used only for system
table
access.
- heap_getnext_slot function is introduced to return the slot whenever the
data is found, otherwise NULL, This function is used in all the places
from
Executor and etc.
- The TupleTableSlot structure is modified to contain a void* tuple instead
of
HeapTuple. And also it contains the storagehanlder functions.
- heap_insert and etc function can take Slot as an argument and perform the
insert operation.
The cases where the TupleTableSlot is not possible to sent, form a HeapTuple
from the data and sent it and also note down that it is a HeapTuple data,
not
the tuple from the storage.
> 2. Design an API that returns StorageTuple(void *) with first member
> > represents the TYPE of the storage, so that corresponding registered
> > function calls can be called to deform/form the tuple whenever there is
> > a need of tuple.
> >
>
> Do you intend to say that we store such information in disk tuple or
> only in the in-memory version of same?
Only in-memory version.
> Also, what makes you think
> that we would need hooks only for form and deform? Right now, in many
> cases tuple will directly point to disk page and we deal with it by
> retaining the pin on the corresponding buffer, what if some kinds of
> tuple don't follow that rule? For ex. to support in-place updates, we
> might always need a separate copy of tuple rather than the one
> pointing to disk page.
>
In any of the approaches, except for system tables, we are going to remove
the direct disk access of the tuple. Either with replace tuple with slot or
something,
no direct disk access will be removed. Otherwise it will be difficult to
support,
and also it doesn't provide much performance benefit also.
All the storagehandler functions needs to be maintained seperately
and accessed by them using the hanlder ID.
heap_getnext function returns StorageTuple and not HeapTuple.
The StorageTuple can be mapped to HeapTuple or another Tuple
based on the first member type.
heap_insert etc function takes input as StorageTuple and internally
decides based on the type of the tuple to perform the insert operation.
Instead of storing the handler functions inside a relation/slot, the
function pointers can be accessed directly based on the storage
type data.
This works every case where the tuple is accessed, but the problem
is, it may need changes wherever the tuple is accessed.
> > 3. Design an API that returns StorageTuple(void *) but the necessary
> > format information of that tuple can be get from the tupledesc. wherever
> > the tuple is present, there exists a tupledesc in most of the cases. How
> > about adding some kind of information in tupledesc to find out the tuple
> > format and call the necessary functions
> >
>
heap_getnext function returns StorageTuple instead of HeapTuple. The tuple
type information is available in the TupleDesc structure.
All heap_insert and etc function accepts TupleTableSlot as input and perform
the insert operation. This approach is almost same as first approach except
the
storage handler functions are stored in TupleDesc.
In case if the tuple is formed internally based on the value/nulls array,
the formed
tuple is always the HeapTuple format and the same is updated in TupleDesc.
I am having doubt that passing the StorageTuple everywhere in case that
location
doesn't contains a TupleDesc and direct access of the tuple may give
problems.
> > 4. Design an API to return always the StorageTuple and it converts to
> > HeapTuple with a function hook if it gets registered (for heap storages
> > this is not required to register the hook, because it is already a
> HeapTuple
> > format). This function hook should be placed in the heap form/deform
> > functions.
> >
heap_getnext function always returns HeapTuple irrespective of the storage.
That means all other storage modules have to frame the HeapTuple from the
data and send it back to the server.
heap_insert and etc functions can work with same interfaces and convert
the HeapTuple internally and perform the insert operation according to their
storage requirement.
There are not many changes in the structures, just adding the storage
handler
functions.
This approach is simple, but it doesn't provide much benefit in having a
different
storage in my opinion.
I am preferring to go with the Original (first) approach and generate the
HeapTuple
wherever it is necessary in upper layers, but the API will return a
TupleTableSlot
with either void pointer to tuple or value/nulls array.
Please provide your views.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-08-07 13:12 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-08-07 13:12 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Aug 1, 2017 at 1:56 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:
>
>
> On Sun, Jul 23, 2017 at 4:10 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
>>
>
>>
>> > 1. Design an API that returns values/nulls array and convert that into a
>> > HeapTuple whenever it is required in the upper layers. All the existing
>> > heap form/deform tuples are used for every tuple with some adjustments.
>> >
>>
>> So, this would have the additional cost of form/deform. Also, how
>> would it have lesser changes as compare to what you have described
>> earlier?
>
>
> Yes, It have the additional cost of form/deform. It is the same approach
> that
> is described earlier. But I have an intention of modifying everywhere the
> HeapTuple is accessed. But with the other prototype changes of removing
> HeapTuple usage from triggers, I realized that it needs some clear design
> to proceed further, instead of combining those changes with pluggable
> Storage API.
>
> - heap_getnext function is kept as it as and it is used only for system
> table
> access.
> - heap_getnext_slot function is introduced to return the slot whenever the
> data is found, otherwise NULL, This function is used in all the places
> from
> Executor and etc.
>
> - The TupleTableSlot structure is modified to contain a void* tuple instead
> of
> HeapTuple. And also it contains the storagehanlder functions.
> - heap_insert and etc function can take Slot as an argument and perform the
> insert operation.
>
> The cases where the TupleTableSlot is not possible to sent, form a HeapTuple
> from the data and sent it and also note down that it is a HeapTuple data,
> not
> the tuple from the storage.
>
..
>
>
>>
>> > 3. Design an API that returns StorageTuple(void *) but the necessary
>> > format information of that tuple can be get from the tupledesc. wherever
>> > the tuple is present, there exists a tupledesc in most of the cases. How
>> > about adding some kind of information in tupledesc to find out the tuple
>> > format and call the necessary functions
>> >
>
>
> heap_getnext function returns StorageTuple instead of HeapTuple. The tuple
> type information is available in the TupleDesc structure.
>
> All heap_insert and etc function accepts TupleTableSlot as input and perform
> the insert operation. This approach is almost same as first approach except
> the
> storage handler functions are stored in TupleDesc.
>
Why do we need to store handler function in TupleDesc? As of now, the
above patch series has it available in RelationData and
TupleTableSlot, I am not sure if instead of that keeping it in
TupleDesc is a good idea. Which all kind of places require TupleDesc
to contain handler? If those are few places, can we think of passing
it as a parameter?
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-08-12 05:01 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-08-12 05:01 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Aug 7, 2017 at 11:12 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Tue, Aug 1, 2017 at 1:56 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
> >
> >
> > On Sun, Jul 23, 2017 at 4:10 PM, Amit Kapila <amit.kapila16@gmail.com>
> > wrote:
> >>
> >
> >>
> >> > 1. Design an API that returns values/nulls array and convert that
> into a
> >> > HeapTuple whenever it is required in the upper layers. All the
> existing
> >> > heap form/deform tuples are used for every tuple with some
> adjustments.
> >> >
> >>
> >> So, this would have the additional cost of form/deform. Also, how
> >> would it have lesser changes as compare to what you have described
> >> earlier?
> >
> >
> > Yes, It have the additional cost of form/deform. It is the same approach
> > that
> > is described earlier. But I have an intention of modifying everywhere the
> > HeapTuple is accessed. But with the other prototype changes of removing
> > HeapTuple usage from triggers, I realized that it needs some clear design
> > to proceed further, instead of combining those changes with pluggable
> > Storage API.
> >
> > - heap_getnext function is kept as it as and it is used only for system
> > table
> > access.
> > - heap_getnext_slot function is introduced to return the slot whenever
> the
> > data is found, otherwise NULL, This function is used in all the places
> > from
> > Executor and etc.
> >
> > - The TupleTableSlot structure is modified to contain a void* tuple
> instead
> > of
> > HeapTuple. And also it contains the storagehanlder functions.
> > - heap_insert and etc function can take Slot as an argument and perform
> the
> > insert operation.
> >
> > The cases where the TupleTableSlot is not possible to sent, form a
> HeapTuple
> > from the data and sent it and also note down that it is a HeapTuple data,
> > not
> > the tuple from the storage.
> >
> ..
> >
> >
> >>
> >> > 3. Design an API that returns StorageTuple(void *) but the necessary
> >> > format information of that tuple can be get from the tupledesc.
> wherever
> >> > the tuple is present, there exists a tupledesc in most of the cases.
> How
> >> > about adding some kind of information in tupledesc to find out the
> tuple
> >> > format and call the necessary functions
> >> >
> >
> >
> > heap_getnext function returns StorageTuple instead of HeapTuple. The
> tuple
> > type information is available in the TupleDesc structure.
> >
> > All heap_insert and etc function accepts TupleTableSlot as input and
> perform
> > the insert operation. This approach is almost same as first approach
> except
> > the
> > storage handler functions are stored in TupleDesc.
> >
>
> Why do we need to store handler function in TupleDesc? As of now, the
> above patch series has it available in RelationData and
> TupleTableSlot, I am not sure if instead of that keeping it in
> TupleDesc is a good idea. Which all kind of places require TupleDesc
> to contain handler? If those are few places, can we think of passing
> it as a parameter?
Till now I am to able to proceed without adding any storage handler
functions to
TupleDesc structure. Sure, I will try the way of passing as a parameter
when
there is a need of it.
During the progress of the patch, I am facing problems in designing the
storage API
regarding the Buffer. For example To replace all the HeapTupleSatisfiesMVCC
and
related functions with function pointers, In HeapTuple format, the tuple
may belongs
to one buffer, so the buffer is passed to the HeapTupleSatifisifes***
functions along
with buffer, But in case of other storage formats, the single buffer may
not contains
the actual data. This buffer is used to set the Hint bits and mark the
buffer as dirty.
In case if the buffer is not available, the performance may affect for the
following
queries if the hint bits are not set.
And also the Buffer is used to get from heap_fetch, heap_lock_tuple and
related
functions to check the Tuple visibility, but currently returning a buffer
from the above
heap_** function is not possible for other formats. And also for the
HeapTuple data,
the tuple data is copied into palloced buffer instead of pointing directly
to the page.
So, returning a Buffer is a valid or not here?
Currently I am proceeding to remove the Buffer as parameter in the API and
proceed
further, In case if it affects the performance, we need to find out a
different appraoch
in handling the hint bits.
comments?
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-08-13 07:17 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-08-13 07:17 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Aug 12, 2017 at 10:31 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>>
>> Why do we need to store handler function in TupleDesc? As of now, the
>> above patch series has it available in RelationData and
>> TupleTableSlot, I am not sure if instead of that keeping it in
>> TupleDesc is a good idea. Which all kind of places require TupleDesc
>> to contain handler? If those are few places, can we think of passing
>> it as a parameter?
>
>
> Till now I am to able to proceed without adding any storage handler
> functions to
> TupleDesc structure. Sure, I will try the way of passing as a parameter when
> there is a need of it.
>
Okay, I think it is better if you discuss such locations before
directly modifying those.
> During the progress of the patch, I am facing problems in designing the
> storage API
> regarding the Buffer. For example To replace all the HeapTupleSatisfiesMVCC
> and
> related functions with function pointers, In HeapTuple format, the tuple may
> belongs
> to one buffer, so the buffer is passed to the HeapTupleSatifisifes***
> functions along
> with buffer, But in case of other storage formats, the single buffer may not
> contains
> the actual data.
>
Also, it is quite possible that some of the storage Am's don't even
want to return bool as a parameter from HeapTupleSatisfies* API's. I
guess what we need here is to provide a way so that different storage
am's can register their function pointer for an equivalent to
satisfies function. So, we need to change
SnapshotData.SnapshotSatisfiesFunc in some way so that different
handlers can register their function instead of using that directly.
I think that should address the problem you are planning to solve by
omitting buffer parameter.
> This buffer is used to set the Hint bits and mark the
> buffer as dirty.
> In case if the buffer is not available, the performance may affect for the
> following
> queries if the hint bits are not set.
>
I don't think it is advisable to change that for the current heap.
> And also the Buffer is used to get from heap_fetch, heap_lock_tuple and
> related
> functions to check the Tuple visibility, but currently returning a buffer
> from the above
> heap_** function is not possible for other formats.
>
Why not? I mean if we consider that all the formats we are worried at
this stage have TID (block number, tuple location), then we can get
the buffer. We might want to consider passing TID as a parameter to
these API's if required to make that possible. You also agreed above
[1] that we can first design the API considering storage formats
having TID.
> And also for the
> HeapTuple data,
> the tuple data is copied into palloced buffer instead of pointing directly
> to the page.
> So, returning a Buffer is a valid or not here?
>
Yeah, but I think for the sake of compatibility and not changing too
much in the current API's signature, we should try to avoid it.
> Currently I am proceeding to remove the Buffer as parameter in the API and
> proceed
> further, In case if it affects the performance, we need to find out a
> different appraoch
> in handling the hint bits.
>
Leaving aside the performance concern, I am not convinced that it is a
good idea to remove Buffer as a parameter from the API's you have
mentioned above. Would you mind thinking once again keeping the
suggestions provided above in this email to see if we can avoid
removing Buffer as a parameter?
[1] - https://www.postgresql.org/message-id/CAJrrPGd8%2Bi8sqZCdhfvBhs2d1akEb_kEuBvgRHSPJ9z2Z7VBJw%40mail.g...
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-08-21 07:28 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-08-21 07:28 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sun, Aug 13, 2017 at 5:17 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Sat, Aug 12, 2017 at 10:31 AM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> >>
> >> Why do we need to store handler function in TupleDesc? As of now, the
> >> above patch series has it available in RelationData and
> >> TupleTableSlot, I am not sure if instead of that keeping it in
> >> TupleDesc is a good idea. Which all kind of places require TupleDesc
> >> to contain handler? If those are few places, can we think of passing
> >> it as a parameter?
> >
> >
> > Till now I am to able to proceed without adding any storage handler
> > functions to
> > TupleDesc structure. Sure, I will try the way of passing as a parameter
> when
> > there is a need of it.
> >
>
> Okay, I think it is better if you discuss such locations before
> directly modifying those.
>
Sure. I will check with community before making any such changes.
> > During the progress of the patch, I am facing problems in designing the
> > storage API
> > regarding the Buffer. For example To replace all the
> HeapTupleSatisfiesMVCC
> > and
> > related functions with function pointers, In HeapTuple format, the tuple
> may
> > belongs
> > to one buffer, so the buffer is passed to the HeapTupleSatifisifes***
> > functions along
> > with buffer, But in case of other storage formats, the single buffer may
> not
> > contains
> > the actual data.
> >
>
> Also, it is quite possible that some of the storage Am's don't even
> want to return bool as a parameter from HeapTupleSatisfies* API's. I
> guess what we need here is to provide a way so that different storage
> am's can register their function pointer for an equivalent to
> satisfies function. So, we need to change
> SnapshotData.SnapshotSatisfiesFunc in some way so that different
> handlers can register their function instead of using that directly.
> I think that should address the problem you are planning to solve by
> omitting buffer parameter.
>
Thanks for your suggestion. Yes, it is better to go in the direction of
SnapshotSatisfiesFunc.
I verified the above idea of implementing the Tuple visibility functions
and assign them into the snapshotData structure based on the snapshot.
The Tuple visibility functions that are specific to the relation are
available
with the RelationData structure and this structure may not be available,
so I changed the SnapShotData structure to hold an enum to represent
what type of snapshot it is, instead of storing the pointer to the tuple
visibility function. Whenever there is a need to check for the tuple
visibilty
the storageam handler pointer corresponding to the snapshot type is
called and result is obtained as earlier.
> This buffer is used to set the Hint bits and mark the
> > buffer as dirty.
> > In case if the buffer is not available, the performance may affect for
> the
> > following
> > queries if the hint bits are not set.
> >
>
> I don't think it is advisable to change that for the current heap.
>
I didn't change the prototype of existing functions. Currently tuple
visibility
functions assumes that Buffer is always proper, but that may not be correct
based on the storage.
>
> > And also the Buffer is used to get from heap_fetch, heap_lock_tuple and
> > related
> > functions to check the Tuple visibility, but currently returning a buffer
> > from the above
> > heap_** function is not possible for other formats.
> >
>
> Why not? I mean if we consider that all the formats we are worried at
> this stage have TID (block number, tuple location), then we can get
> the buffer. We might want to consider passing TID as a parameter to
> these API's if required to make that possible. You also agreed above
> [1] that we can first design the API considering storage formats
> having TID.
>
The current approach is to support the storages that support TID bits.
But what I mean here, in some storage methods (for example column
storage), the tuple is not present in one buffer, the tuple data may be
calculated from many buffers and return the slot/storageTuple (until
unless we change everywhere to slot).
If any of the following code after the storage methods is expecting
a Buffer that should be valid may need some changes to check it first
whether it is a valid or not and perform the operations based on that.
> > And also for the
> > HeapTuple data,
> > the tuple data is copied into palloced buffer instead of pointing
> directly
> > to the page.
> > So, returning a Buffer is a valid or not here?
> >
>
> Yeah, but I think for the sake of compatibility and not changing too
> much in the current API's signature, we should try to avoid it.
>
Currently I am trying to avoid changing the current API's signatures.
Most of the signature changes are something like HeapTuple -> StorageTuple
and etc.
> > Currently I am proceeding to remove the Buffer as parameter in the API
> and
> > proceed
> > further, In case if it affects the performance, we need to find out a
> > different appraoch
> > in handling the hint bits.
> >
>
> Leaving aside the performance concern, I am not convinced that it is a
> good idea to remove Buffer as a parameter from the API's you have
> mentioned above. Would you mind thinking once again keeping the
> suggestions provided above in this email to see if we can avoid
> removing Buffer as a parameter?
Thanks for your suggestions.
Yes. I am able to proceed without removing Buffer parameter.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-08-21 09:25 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-08-21 09:25 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Aug 21, 2017 at 12:58 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
> On Sun, Aug 13, 2017 at 5:17 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
>>
>>
>> Also, it is quite possible that some of the storage Am's don't even
>> want to return bool as a parameter from HeapTupleSatisfies* API's. I
>> guess what we need here is to provide a way so that different storage
>> am's can register their function pointer for an equivalent to
>> satisfies function. So, we need to change
>> SnapshotData.SnapshotSatisfiesFunc in some way so that different
>> handlers can register their function instead of using that directly.
>> I think that should address the problem you are planning to solve by
>> omitting buffer parameter.
>
>
> Thanks for your suggestion. Yes, it is better to go in the direction of
> SnapshotSatisfiesFunc.
>
> I verified the above idea of implementing the Tuple visibility functions
> and assign them into the snapshotData structure based on the snapshot.
>
> The Tuple visibility functions that are specific to the relation are
> available
> with the RelationData structure and this structure may not be available,
>
Which functions are you referring here? I don't see anything in
tqual.h that uses RelationData.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-08-23 05:35 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-08-23 05:35 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Aug 21, 2017 at 7:25 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Mon, Aug 21, 2017 at 12:58 PM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> >
> > On Sun, Aug 13, 2017 at 5:17 PM, Amit Kapila <amit.kapila16@gmail.com>
> > wrote:
> >>
> >>
> >> Also, it is quite possible that some of the storage Am's don't even
> >> want to return bool as a parameter from HeapTupleSatisfies* API's. I
> >> guess what we need here is to provide a way so that different storage
> >> am's can register their function pointer for an equivalent to
> >> satisfies function. So, we need to change
> >> SnapshotData.SnapshotSatisfiesFunc in some way so that different
> >> handlers can register their function instead of using that directly.
> >> I think that should address the problem you are planning to solve by
> >> omitting buffer parameter.
> >
> >
> > Thanks for your suggestion. Yes, it is better to go in the direction of
> > SnapshotSatisfiesFunc.
> >
> > I verified the above idea of implementing the Tuple visibility functions
> > and assign them into the snapshotData structure based on the snapshot.
> >
> > The Tuple visibility functions that are specific to the relation are
> > available
> > with the RelationData structure and this structure may not be available,
> >
>
> Which functions are you referring here? I don't see anything in
> tqual.h that uses RelationData.
With storage API's, the tuple visibility functions are available with
RelationData
and those are needs used to update the SnapshotData structure
SnapshotSatisfiesFunc member.
But the RelationData is not available everywhere, where the snapshot is
created,
but it is available every place where the tuple visibility is checked. So I
just changed
the way of checking the tuple visibility with the information of snapshot
by calling
the corresponding tuple visibility function from RelationData.
If SnapshotData provides MVCC, then the MVCC specific tuple visibility
function from
RelationData is called. The SnapshotSatisfiesFunc member is changed to a
enum
that holds the tuple visibility type such as MVCC, DIRTY, SELF and etc.
Whenever
the visibility check is needed, the corresponding function is called.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-08-23 13:59 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-08-23 13:59 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Aug 23, 2017 at 11:05 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
>
> On Mon, Aug 21, 2017 at 7:25 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
>>
>> On Mon, Aug 21, 2017 at 12:58 PM, Haribabu Kommi
>> <kommi.haribabu@gmail.com> wrote:
>> >
>> > On Sun, Aug 13, 2017 at 5:17 PM, Amit Kapila <amit.kapila16@gmail.com>
>> > wrote:
>> >>
>> >>
>> >> Also, it is quite possible that some of the storage Am's don't even
>> >> want to return bool as a parameter from HeapTupleSatisfies* API's. I
>> >> guess what we need here is to provide a way so that different storage
>> >> am's can register their function pointer for an equivalent to
>> >> satisfies function. So, we need to change
>> >> SnapshotData.SnapshotSatisfiesFunc in some way so that different
>> >> handlers can register their function instead of using that directly.
>> >> I think that should address the problem you are planning to solve by
>> >> omitting buffer parameter.
>> >
>> >
>> > Thanks for your suggestion. Yes, it is better to go in the direction of
>> > SnapshotSatisfiesFunc.
>> >
>> > I verified the above idea of implementing the Tuple visibility functions
>> > and assign them into the snapshotData structure based on the snapshot.
>> >
>> > The Tuple visibility functions that are specific to the relation are
>> > available
>> > with the RelationData structure and this structure may not be available,
>> >
>>
>> Which functions are you referring here? I don't see anything in
>> tqual.h that uses RelationData.
>
>
>
> With storage API's, the tuple visibility functions are available with
> RelationData
> and those are needs used to update the SnapshotData structure
> SnapshotSatisfiesFunc member.
>
> But the RelationData is not available everywhere, where the snapshot is
> created,
> but it is available every place where the tuple visibility is checked. So I
> just changed
> the way of checking the tuple visibility with the information of snapshot by
> calling
> the corresponding tuple visibility function from RelationData.
>
> If SnapshotData provides MVCC, then the MVCC specific tuple visibility
> function from
> RelationData is called. The SnapshotSatisfiesFunc member is changed to a
> enum
> that holds the tuple visibility type such as MVCC, DIRTY, SELF and etc.
> Whenever
> the visibility check is needed, the corresponding function is called.
>
It will be easy to understand and see if there is some better
alternative once you have something in the form of a patch.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-08-26 03:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-08-26 03:34 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Aug 23, 2017 at 11:59 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Wed, Aug 23, 2017 at 11:05 AM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> >
> >
> > On Mon, Aug 21, 2017 at 7:25 PM, Amit Kapila <amit.kapila16@gmail.com>
> > wrote:
> >>
> >> On Mon, Aug 21, 2017 at 12:58 PM, Haribabu Kommi
> >> <kommi.haribabu@gmail.com> wrote:
> >> >
> >> > On Sun, Aug 13, 2017 at 5:17 PM, Amit Kapila <amit.kapila16@gmail.com
> >
> >> > wrote:
> >> >>
> >> >>
> >> >> Also, it is quite possible that some of the storage Am's don't even
> >> >> want to return bool as a parameter from HeapTupleSatisfies* API's. I
> >> >> guess what we need here is to provide a way so that different storage
> >> >> am's can register their function pointer for an equivalent to
> >> >> satisfies function. So, we need to change
> >> >> SnapshotData.SnapshotSatisfiesFunc in some way so that different
> >> >> handlers can register their function instead of using that directly.
> >> >> I think that should address the problem you are planning to solve by
> >> >> omitting buffer parameter.
> >> >
> >> >
> >> > Thanks for your suggestion. Yes, it is better to go in the direction
> of
> >> > SnapshotSatisfiesFunc.
> >> >
> >> > I verified the above idea of implementing the Tuple visibility
> functions
> >> > and assign them into the snapshotData structure based on the snapshot.
> >> >
> >> > The Tuple visibility functions that are specific to the relation are
> >> > available
> >> > with the RelationData structure and this structure may not be
> available,
> >> >
> >>
> >> Which functions are you referring here? I don't see anything in
> >> tqual.h that uses RelationData.
> >
> >
> >
> > With storage API's, the tuple visibility functions are available with
> > RelationData
> > and those are needs used to update the SnapshotData structure
> > SnapshotSatisfiesFunc member.
> >
> > But the RelationData is not available everywhere, where the snapshot is
> > created,
> > but it is available every place where the tuple visibility is checked.
> So I
> > just changed
> > the way of checking the tuple visibility with the information of
> snapshot by
> > calling
> > the corresponding tuple visibility function from RelationData.
> >
> > If SnapshotData provides MVCC, then the MVCC specific tuple visibility
> > function from
> > RelationData is called. The SnapshotSatisfiesFunc member is changed to a
> > enum
> > that holds the tuple visibility type such as MVCC, DIRTY, SELF and etc.
> > Whenever
> > the visibility check is needed, the corresponding function is called.
> >
>
> It will be easy to understand and see if there is some better
> alternative once you have something in the form of a patch.
Sorry for the delay.
I will submit the new patch series with all comments given
in the upthread to the upcoming commitfest.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-01 01:51 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-09-01 01:51 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Aug 26, 2017 at 1:34 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> I will submit the new patch series with all comments given
> in the upthread to the upcoming commitfest.
>
Here I attached new set of patches that are rebased to the latest master.
0001-Change-Create-Access-method-to-include-storage-handl:
Add the support of storage method to create as part of create
access method syntax.
0002-Storage-AM-API-hooks-and-related-functions:
The necessary storage AM API hooks that are required
to support pluggable storage API and supporting functions
to frame the storage routine.
0003-Adding-storageam-hanlder-to-relation-structure:
Add the storageAm routine pointer to relation structure
and necessary functions to initialize the storage AM routine
whenever the relation is built.
0004-Adding-tuple-visibility-function-to-storage-AM:
The tuple visibility functions are moved into heap storage AM
and the visibility function in snapshot structure is changed from
function pointer to an enum to indicate what type of snapshot
it is. Based on that enum, the corresponding visibility function
is executed from the relation storage AM routine.
0005-slot-hooks-are-added-to-storage-AM:
The slot specific storage AM routine pointer is added to slot
structure and removed some of the members and created
a new HeapamSlot structure to hold the necessary tuple
information. Currently the slot supports the minimal tuple
also.
Currently this patch may further needs some changes as it
assumes the tuple is in HeapTuple format in some API's.
This slot storage AM routine may be common to all the
pluggable storage modules.
0006-Tuple-Insert-API-is-added-to-Storage-AM:
The write support functionality is added to storage AM.
And also all the storage AM functions are extracted into
another file to make it easier to understand while writing
and changing the code to support new API's.
0007-Scan-functions-are-added-to-storage-AM:
All the scan supported functions are added to storage AM.
And these functions are also extracted into storageam.c file.
Pending comments from Andres:
1. Remove the usage of HeapScanDesc.
2. Remove the usage of scan_getpage, scan_update_snapshot
and scan_rescan_set_params hooks.
3. Many places the relation is not available while creating the slot,
and also slot shouldn't depend on relation, because the slot values
are may be some times from two different storage relations also.
So I modified the slot code to use the same storage mechanism.
4.Tuples functionality moving into a separate folder.
Other pending activities are:
1. Handle Bitmap and sample scans as they dependent on heap format.
2. Add some configuration flags in the storage AM, based on these flags
whether vacuum can run on these relations or not will be decided. This
may be further enhanced to provide the cost parameters also that can be
used for planning.
I attached individual patches in the mail, in case if it increases the mail
size,
or creates problems to some one, i will attach the zip version from next
time
onward.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (177.8K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/3-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 2c2b95107ccc45ab44eaf5e155ba1f44fe17c5c4 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 19:00:19 +1000
Subject: [PATCH 7/7] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1509 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1303 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 2 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 7 +-
src/backend/access/index/indexam.c | 3 +-
src/backend/access/nbtree/nbtinsert.c | 5 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 27 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 13 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 15 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 11 +-
src/backend/executor/nodeForeignscan.c | 7 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 12 +-
src/backend/executor/nodeIndexonlyscan.c | 4 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 21 +-
src/backend/executor/nodeSeqscan.c | 39 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 16 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 29 -
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 42 +-
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 10 +-
src/include/executor/tqueue.h | 2 +-
src/include/funcapi.h | 2 +-
55 files changed, 1920 insertions(+), 1740 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 5f076ef..063e079 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index f7b68a8..eb33b26 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 1eb2c32..4146015 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,603 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
@@ -1188,332 +445,97 @@ relation_close(Relation relation, LOCKMODE lockmode)
/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
-}
-
-/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
- /*
- * reset parallel scan, if present
- */
- if (scan->rs_parallel != NULL)
- {
- ParallelHeapScanDesc parallel_scan;
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
- /*
- * Caller is responsible for making sure that all workers have
- * finished the scan before calling this.
- */
- parallel_scan = scan->rs_parallel;
- pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
- }
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
+
/* ----------------
* heap_parallelscan_estimate - estimate storage for ParallelHeapScanDesc
*
@@ -1552,379 +574,6 @@ heap_parallelscan_initialize(ParallelHeapScanDesc target, Relation relation,
SerializeSnapshot(snapshot, target->phs_snapshot_data);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return &(scan->rs_ctup);
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
/*
@@ -4761,32 +3410,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index d08a99e..6d031d1 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1640,6 +1640,1095 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
+
/*
* heapam_fetch - retrieve tuple with given tid
@@ -1796,7 +2885,210 @@ heapam_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
@@ -3762,6 +5054,17 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 8fba61c..a475a85 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,7 +409,7 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
state->rs_cutoff_multi);
/*
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index f8e7a4f..53b3b3c 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot*
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2..db5c93b 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,7 +395,7 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
nkeys, key,
true, false);
sysscan->iscan = NULL;
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255..349a127 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,7 +606,7 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
scan->xs_cbuf,
scan->xs_snapshot,
&scan->xs_ctup,
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fc..0e25e9a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,7 +326,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
&all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4..975cd5b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
@@ -573,18 +574,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -592,7 +593,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -903,25 +904,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a..d2a8a06 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0240df7..68c46f7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,7 +2280,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -2288,7 +2289,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
true); /* syncscan OK */
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,7 +3005,7 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -3014,7 +3015,7 @@ validate_index_heapscan(Relation heapRelation,
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..1d048e6 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..517e310 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8..145e3c1 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71..a51f2e4 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc..e0f6973 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e5..6727d15 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c81ddf5..00e71e3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2028,10 +2028,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2043,7 +2043,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539..39850b1 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b61aaac..46bc3da 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1948,8 +1949,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1989,7 +1990,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d46e2e6..c3e90bd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4496,7 +4496,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4504,7 +4504,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4610,7 +4610,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5013,9 +5013,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5027,7 +5027,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8161,7 +8161,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8169,7 +8169,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8184,7 +8184,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8239,9 +8239,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8270,7 +8270,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10720,8 +10720,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10780,7 +10780,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b..cdfa8ff 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d2438ce..fbbdbc2 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16ae..c07f508 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2315,8 +2316,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2345,7 +2346,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2711,8 +2712,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2755,7 +2756,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index faa1812..e24ac9f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -447,9 +448,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -469,7 +470,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
}
@@ -1121,9 +1122,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1160,7 +1161,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5..6ade9df 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189f..5e9daea 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index a1c2319..dd425d4 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,19 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot)) != NULL)
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +312,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b683ac6..d3d0f1b 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -681,7 +681,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -765,7 +765,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1085,7 +1085,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1093,7 +1093,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index b7ac5f7..db6b5ad 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0ae5873..d94169c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3097,7 +3097,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3212,7 +3212,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 02e8f3d..d4f46cb 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
@@ -400,7 +401,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
@@ -685,7 +686,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -781,7 +782,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -882,7 +883,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
estate->es_snapshot,
0,
NULL);
@@ -1015,5 +1016,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 84ff343..f25133c 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -61,11 +61,8 @@ ForeignNext(ForeignScanState *node)
* tableoid, which is the only actually-useful system column.
*/
if (plan->fsSystemCol && !TupIsNull(slot))
- {
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
- }
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
return slot;
}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index a0f5a60..abd7580 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -250,7 +250,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->reader != NULL || gatherstate->need_to_scan_locally)
{
@@ -293,7 +293,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -301,7 +301,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 2526c58..673fa44 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -31,7 +31,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple;
+ StorageTuple *tuple;
int readCounter;
int nTuples;
bool done;
@@ -47,7 +47,7 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
bool nowait, bool *done);
static void gather_merge_init(GatherMergeState *gm_state);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
@@ -377,7 +377,7 @@ gather_merge_init(GatherMergeState *gm_state)
{
/* Allocate the tuple array with MAX_TUPLE_STORE size */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize slot for worker */
gm_state->gm_slots[i] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -541,7 +541,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup = NULL;
+ StorageTuple tup = NULL;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -616,12 +616,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given reader.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup = NULL;
+ StorageTuple tup = NULL;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index fe7ba3f..8668aa5 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -116,7 +116,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -185,7 +185,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
+ * provided in either StorageTuple or IndexTuple format. Conceivably an
* index AM might fill both fields, in which case we prefer the heap
* format, since it's probably a bit cheaper to fill a slot from.
*/
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 404076d..38be6b1 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -50,7 +50,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -64,9 +64,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -83,7 +83,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -184,7 +184,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -482,7 +482,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -515,10 +515,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1..04f85e5 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+ HeapScanDesc scan); //hari
/* ----------------------------------------------------------------
* Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -244,7 +245,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,7 +350,7 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
scanstate->ss.ps.state->es_snapshot,
0, NULL,
scanstate->use_bulkread,
@@ -358,7 +359,7 @@ tablesample_init(SampleScanState *scanstate)
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
@@ -376,7 +377,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +555,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 5c49d4c..77f171f 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -27,6 +27,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -48,8 +49,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -68,7 +68,7 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
estate->es_snapshot,
0, NULL);
node->ss.ss_currentScanDesc = scandesc;
@@ -77,26 +77,7 @@ SeqNext(SeqScanState *node)
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -224,7 +205,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -247,7 +228,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -269,12 +250,12 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
+ storage_rescan(scan, /* scan desc */
NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
@@ -321,7 +302,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -337,5 +318,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 80be460..d55a752 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2091,7 +2091,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2159,7 +2159,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index afe231f..418c2a6 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -854,7 +854,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -962,7 +962,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1683,7 +1683,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1714,8 +1714,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index a8e5206..3440db2 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -192,12 +192,12 @@ static void TQSendRecordInfo(TQueueDestReceiver *tqueue, int32 typmod,
TupleDesc tupledesc);
static void TupleQueueHandleControlMessage(TupleQueueReader *reader,
Size nbytes, char *data);
-static HeapTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
+static StorageTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes, HeapTupleHeader data);
-static HeapTuple TQRemapTuple(TupleQueueReader *reader,
+static StorageTuple TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple);
+ StorageTuple tuple);
static Datum TQRemap(TupleQueueReader *reader, TupleRemapInfo *remapinfo,
Datum value, bool *changed);
static Datum TQRemapArray(TupleQueueReader *reader, ArrayRemapInfo *remapinfo,
@@ -675,7 +675,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
shm_mq_result result;
@@ -730,7 +730,7 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
/*
* Handle a data message - that is, a tuple - from the remote side.
*/
-static HeapTuple
+static StorageTuple
TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes,
HeapTupleHeader data)
@@ -759,11 +759,11 @@ TupleQueueHandleDataMessage(TupleQueueReader *reader,
/*
* Copy the given tuple, remapping any transient typmods contained in it.
*/
-static HeapTuple
+static StorageTuple
TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple)
+ StorageTuple tuple)
{
Datum *values;
bool *isnull;
@@ -1121,7 +1121,7 @@ TupleQueueHandleControlMessage(TupleQueueReader *reader, Size nbytes,
static TupleRemapInfo *
BuildTupleRemapInfo(Oid typid, MemoryContext mycontext)
{
- HeapTuple tup;
+ StorageTuple tup;
Form_pg_type typ;
/* This is recursive, so it could be driven to stack overflow. */
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..fec203d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1882,9 +1882,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1911,7 +1911,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2042,13 +2042,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2134,7 +2134,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2142,8 +2142,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2189,7 +2189,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 1f75e2e..6370541 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 6c89442..fca56d4 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index d03984a..d71d673 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index eb6960d..82c042a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1208,10 +1209,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index b40d2d5..7f8b908 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,39 +106,14 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
@@ -146,8 +121,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -157,8 +130,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 799b4ed..66a96d7 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 9502c92..507af71 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,6 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -36,6 +37,34 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -44,6 +73,15 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
@@ -72,10 +110,6 @@ extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
-extern void storage_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid);
-
extern void storage_sync(Relation rel);
#endif
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d894..7f9bef1 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e..d466c99 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index a717ac6..4156767 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -27,7 +27,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle,
TupleDesc tupledesc);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2a..ab0e091 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
1.8.3.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.6K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/4-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From c877ea9cdbd82efae869db9e73eb4d669be34e32 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/7] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 4 ++++
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa..33079c1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7d0de99..ee8760e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5143,16 +5145,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be79353..0a7e0a3 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b..2fbe359 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
@@ -83,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 772 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 772
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e..a38a954 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3826,6 +3826,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 336 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 337 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452..ea352fa 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7..3bc9607 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1711,11 +1711,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 2945966..f1f58a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1153,15 +1153,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
1.8.3.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.4K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/5-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From 0be15e2d9f0023068d44815e28708f0c42d6d430 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/7] Storage AM API hooks and related functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 237 +++++++++++++++++++++++++++++++
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
8 files changed, 427 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496..02a3909 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000..fbffb3a
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000..def2029
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine *HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68..6459435 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000..95fe028
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,237 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*scan_end_hook) (HeapScanDesc scan);
+
+
+typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*tuple_insert_hook) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*tuple_delete_hook) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*tuple_update_hook) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*tuple_fetch_hook) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*tuple_lock_hook) (Relation relation,
+ ItemPointer tid,
+ StorageTuple *tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*multi_insert_hook) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*tuple_freeze_hook) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*tuple_get_latest_tid_hook) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data (*get_tuple_data_hook) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple (*tuple_from_datum_hook) (Datum data, Oid tableoid);
+
+typedef bool (*tuple_is_heaponly_hook) (StorageTuple tuple);
+
+typedef void (*slot_store_tuple_hook) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*slot_clear_tuple_hook) (TupleTableSlot *slot);
+typedef Datum (*slot_getattr_hook) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*slot_virtualize_tuple_hook) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*slot_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*slot_min_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*slot_update_tableoid_hook) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*speculative_finish_hook) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*speculative_abort_hook) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*relation_sync_hook) (Relation relation);
+
+typedef bool (*snapshot_satisfies_hook) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*snapshot_satisfies_update_hook) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*snapshot_satisfies_vacuum_hook) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ slot_store_tuple_hook slot_store_tuple;
+ slot_virtualize_tuple_hook slot_virtualize_tuple;
+ slot_clear_tuple_hook slot_clear_tuple;
+ slot_getattr_hook slot_getattr;
+ slot_tuple_hook slot_tuple;
+ slot_min_tuple_hook slot_min_tuple;
+ slot_update_tableoid_hook slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine* (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ scan_begin_hook scan_begin;
+ scan_setlimits_hook scansetlimits;
+ scan_getnext_hook scan_getnext;
+ scan_getnext_slot_hook scan_getnextslot;
+ scan_end_hook scan_end;
+ scan_getpage_hook scan_getpage;
+ scan_rescan_hook scan_rescan;
+ scan_update_snapshot_hook scan_update_snapshot;
+ hot_search_buffer_hook hot_search_buffer; /* heap_hot_search_buffer */
+
+ // heap_sync_function heap_sync; /* heap_sync */
+ /* not implemented */
+ // parallelscan_estimate_function parallelscan_estimate; /* heap_parallelscan_estimate */
+ // parallelscan_initialize_function parallelscan_initialize; /* heap_parallelscan_initialize */
+ // parallelscan_begin_function parallelscan_begin; /* heap_beginscan_parallel */
+
+ /* Operations on physical tuples */
+ tuple_insert_hook tuple_insert; /* heap_insert */
+ tuple_update_hook tuple_update; /* heap_update */
+ tuple_delete_hook tuple_delete; /* heap_delete */
+ tuple_fetch_hook tuple_fetch; /* heap_fetch */
+ tuple_lock_hook tuple_lock; /* heap_lock_tuple */
+ multi_insert_hook multi_insert; /* heap_multi_insert */
+ tuple_freeze_hook tuple_freeze; /* heap_freeze_tuple */
+ tuple_get_latest_tid_hook tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ get_tuple_data_hook get_tuple_data;
+ tuple_is_heaponly_hook tuple_is_heaponly;
+ tuple_from_datum_hook tuple_from_datum;
+
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no need
+ * for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ speculative_finish_hook speculative_finish;
+ speculative_abort_hook speculative_abort;
+
+
+ relation_sync_hook relation_sync; /* heap_sync */
+
+ snapshot_satisfies_hook snapshot_satisfies[END_OF_VISIBILITY];
+ snapshot_satisfies_update_hook snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ snapshot_satisfies_vacuum_hook snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine *GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine *GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine *GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a38a954..1065671 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -549,6 +549,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 425 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 425
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3..108df9b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -496,6 +496,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 036d989..0fb2e1c 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
1.8.3.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/6-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From be49e85d588d142749c4c2fb3636ac17909513a8 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/7] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 117 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e3780..40cbc67 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1885,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2027,6 +2109,11 @@ formrdesc(const char *relationName, Oid relationReltype,
RelationInitPhysicalAddr(relation);
/*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
+ /*
* initialize the rel-has-index flag, using hardwired knowledge
*/
if (IsBootstrapProcessingMode())
@@ -2354,6 +2441,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3457,13 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3985,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert (relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5715,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..82d8a53 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -161,6 +161,12 @@ typedef struct RelationData
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
+ /*
* index access support info (used only for an index relation)
*
* Note: only default support procs for each opclass are cached, namely
@@ -428,6 +434,12 @@ typedef struct ViewOptions
#define RelationGetDescr(relation) ((relation)->rd_att)
/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
+/*
* RelationGetRelationName
* Returns the rel's name.
*
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cef..03d996f 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
1.8.3.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (144.5K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/7-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From d3259b65df625091305a0223550020be649362af Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 13:59:59 +1000
Subject: [PATCH 4/7] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 10 +-
contrib/pgrowlocks/pgrowlocks.c | 3 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 23 +-
src/backend/access/heap/heapam_common.c | 162 +++
src/backend/access/heap/heapam_storage.c | 1601 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 4 +-
src/backend/commands/analyze.c | 2 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 7 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1787 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 62 +-
28 files changed, 1940 insertions(+), 1881 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575..10d47cd 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -51,7 +51,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +656,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +681,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +739,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65..5f076ef 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,7 +150,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
GetCurrentCommandId(false),
scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf0613..284eabc 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7a91cc3..f7b68a8 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909..e6bc18e 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index ff03c68..57aab19 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -1952,7 +1955,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2099,7 +2102,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2270,7 +2273,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3094,7 +3097,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3205,7 +3208,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3665,7 +3668,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3846,7 +3849,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4597,7 +4600,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000..a1d9859
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
+
+
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index fbffb3a..77a5721 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,6 +48,1597 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except for known-committed
+ * XIDs which could not be ours anyway.
+ */
+static bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
@@ -54,5 +1646,14 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
+ amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
+ amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
+ amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
+ amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
+ amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] = HeapTupleSatisfiesHistoricMVCC;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac..7425068 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index bd560e4..191f088 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da0..01321a2 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f03..0240df7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,7 +2359,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index fbad13e..255a797 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1069,7 +1069,7 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
OldestXmin,
targbuffer))
{
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e..dbcc5bc 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 45b1859..dccda5a 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -975,7 +975,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2140,7 +2140,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 79f534e..02e8f3d 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -428,7 +428,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e12721a..daa7c7b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1307,7 +1308,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a83..6a118d1 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index fba57a0..095d1ed 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 6a6d9d6..22948cb 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6..5a6d216 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa..f17b1c5 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 08a08c8..1f96913 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2040,7 +2040,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2137,7 +2137,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index f9da9e1..0000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1787 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-/* local functions */
-static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except for known-committed
- * XIDs which could not be ours anyway.
- */
-static bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000..ff63cf3
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc..b8b823b 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 074cc81..96f14ec 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -51,7 +51,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 0fb2e1c..ed8f644 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,46 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
-
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies[(snapshot)->visibility_type]) (tuple, snapshot, buffer))
/*
* We don't provide a static SnapshotDirty variable because it would be
@@ -89,15 +52,26 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for SnapshotToast. We need
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
1.8.3.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (60.6K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/8-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 43187d9260a7644c56f5a71d19eecb659f67acb7 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 17:24:35 +1000
Subject: [PATCH 5/7] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 1 +
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 15 +-
src/backend/executor/execExprInterp.c | 26 +-
src/backend/executor/execReplication.c | 92 ++-----
src/backend/executor/execTuples.c | 269 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 644 insertions(+), 575 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528..5ed0f15 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1022,111 +1023,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
-/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
* It is functionally equivalent to heap_getattr, but fetches of
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index a1d9859..06a7af1 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -159,4 +159,408 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *)palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple)tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple)tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine*
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 77a5721..bf22f2e 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1645,6 +1645,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->slot_storageam = heapam_storage_slot_handler;
amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cfa3f05..8456bfd 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2695,7 +2695,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210c..a0ec444 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376..b440740 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index da0850b..8634473 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2348,7 +2348,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2429,7 +2429,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2777,7 +2777,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2819,7 +2819,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2928,7 +2928,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3960,14 +3960,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 83e0447..ad81ab9 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -503,12 +503,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+ //hari Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(innerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -519,12 +519,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
-
+ Assert(outerslot->tts_storage != NULL);
+ //hari Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
+
+ *op->resvalue = slot_getattr(outerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -535,12 +535,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+ //hari Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(scanslot,
+ attnum,
op->resnull);
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fbb8108..d424074 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,7 +226,8 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ TupleTableSlot *scanslot;
+ HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
TransactionId xwait;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 31f814c..b683ac6 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -206,6 +206,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
}
/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert (slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
+/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,18 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
-
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +459,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +540,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +549,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +568,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +598,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +645,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +653,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +664,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +697,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +717,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,19 +728,11 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
/*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
* mustn't leave any dangling pass-by-reference datums in tts_values.
@@ -768,17 +741,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 140e82e..84ff343 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index daa7c7b..f7051b3 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -403,7 +403,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -416,7 +416,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -434,7 +434,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -743,7 +743,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -894,7 +894,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -955,7 +955,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -974,7 +974,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -988,7 +988,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1004,7 +1004,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1120,7 +1120,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 4c4fcf5..a8e5206 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -305,7 +305,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
}
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 041f387..47dc627 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -724,9 +724,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3..1fe15ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index fa04a63..9539d67 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -21,6 +21,19 @@
#include "storage/bufpage.h"
/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
+/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
* a tuple, plus the size of the null-values bitmap (at 1 bit per column),
@@ -653,7 +666,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce..84dc293 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
1.8.3.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (245.8K, ../../CAJrrPGf-SXvnRfFbuNQ8yJwyXKF5zk9LgKh1E2M-aQfebQmq4g@mail.gmail.com/9-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From 53ea1e536bf34699be7374a1f64bea1e10210c4b Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 17:37:59 +1000
Subject: [PATCH 6/7] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM.
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2737 ++++--------------------------
src/backend/access/heap/heapam_storage.c | 2134 +++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 303 ++++
src/backend/access/heap/tuptoaster.c | 8 +-
src/backend/commands/copy.c | 29 +-
src/backend/commands/createas.c | 18 +-
src/backend/commands/matview.c | 10 +-
src/backend/commands/tablecmds.c | 5 +-
src/backend/commands/trigger.c | 43 +-
src/backend/executor/execMain.c | 112 +-
src/backend/executor/execReplication.c | 35 +-
src/backend/executor/nodeLockRows.c | 39 +-
src/backend/executor/nodeModifyTable.c | 177 +-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/storageam.h | 81 +
src/include/access/storageamapi.h | 21 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 6 +-
src/include/nodes/execnodes.h | 4 +-
24 files changed, 3242 insertions(+), 2710 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e..162736f 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 57aab19..1eb2c32 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1840,158 +1747,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
}
/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
-/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
* On entry, *tid is the TID of a tuple (either a simple tuple, or the root
@@ -2171,130 +1926,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -2312,7 +1943,7 @@ heap_get_latest_tid(Relation relation,
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2595,7 +2226,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2663,412 +2294,110 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
-
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ Assert(ItemPointerIsValid(tid));
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
*/
if (PageIsAllVisible(page))
visibilitymap_pin(relation, block, &vmbuffer);
@@ -4503,7 +3832,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4521,724 +3850,34 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
-
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
+ if (*have_tuple_lock)
+ return true;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode))
@@ -5271,7 +3910,7 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
* window, but it's still possible to end up creating an unnecessary
* MultiXactId. Fortunately this is harmless.
*/
-static void
+void
compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
uint16 old_infomask2, TransactionId add_to_xmax,
LockTupleMode mode, bool is_update,
@@ -5318,916 +3957,226 @@ l5:
break;
case LockTupleNoKeyExclusive:
new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
- }
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
-}
-
-/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
- *
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
- *
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
- *
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
- */
-void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
-{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
-
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
-
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
+}
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
-}
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
@@ -6992,7 +4941,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7159,7 +5108,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7181,7 +5130,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7743,7 +5692,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9120,46 +7069,6 @@ heap2_redo(XLogReaderState *record)
}
/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
-/*
* Mask a heap page before performing consistency checks on it.
*/
void
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index bf22f2e..d08a99e 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1640,11 +1640,2145 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
+
+/*
+ * heapam_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+ HeapTupleData tuple;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ *stuple = NULL;
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ *stuple = NULL;
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple.t_self));
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, &tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, &tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is completely
+ * bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ Assert(slot->tts_speculativeToken != 0);
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTuple mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heapam_fetch(rel, &tupid, SnapshotAny, (StorageTuple *)&mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup->t_data),
+ priorXmax))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup->t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup->t_data->t_infomask;
+ old_infomask2 = mytup->t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup->t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup->t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup->t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup->t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup->t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup->t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup->t_data, new_xmax);
+ mytup->t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup->t_data->t_infomask |= new_infomask;
+ mytup->t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup->t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup->t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup->t_self, &mytup->t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup->t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup->t_data);
+ ItemPointerCopy(&(mytup->t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data)HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data)HeapTupleHeaderGetUpdateXid(((HeapTuple)tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data)HeapTupleHeaderGetCmin(((HeapTuple)tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data)((HeapTuple)tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data)((HeapTuple)tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static bool
+heapam_tuple_is_heaopnly(StorageTuple tuple)
+{
+ return HeapTupleIsHeapOnly((HeapTuple)tuple);
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_finish = heapam_finish_speculative;
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_is_heaponly = heapam_tuple_is_heaopnly;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
+
amroutine->slot_storageam = heapam_storage_slot_handler;
amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 191f088..8fba61c 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000..f8e7a4f
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,303 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid,
+ options, bistate);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, cid,
+ crosscheck, wait, hufd, lockmode);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+
+/*
+ * storage_finish_speculative - mark speculative insertion as successful
+ *
+ * To successfully finish a speculative insertion we have to clear speculative
+ * token from tuple. To do so the t_ctid field, which will contain a
+ * speculative token value, is modified in place to point to the tuple itself,
+ * which is characteristic of a newly inserted ordinary tuple.
+ *
+ * NB: It is not ok to commit without either finishing or aborting a
+ * speculative insertion. We could treat speculative tuples of committed
+ * transactions implicitly as completed, but then we would have to be prepared
+ * to deal with speculative tokens on committed tuples. That wouldn't be
+ * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
+ * but clearing the token at completion isn't very expensive either.
+ * An explicit confirmation WAL record also makes logical decoding simpler.
+ */
+void
+storage_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_finish(relation, slot);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+bool
+storage_tuple_is_heaponly(Relation relation, StorageTuple tuple)
+{
+ return relation->rd_stamroutine->tuple_is_heaponly(tuple);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1da..d766a6e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,12 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8456bfd..c81ddf5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2694,8 +2695,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2758,19 +2757,18 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
+ &(slot->tts_tid),
estate,
false,
NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2866,7 +2864,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2899,12 +2897,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2923,7 +2921,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2940,8 +2938,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index a0ec444..d119149 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,24 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ slot->tts_tupleOid = InvalidOid;
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +619,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740..6102481 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,16 +492,15 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
- heap_insert(myState->transientrel,
- tuple,
+ storage_insert(myState->transientrel,
+ slot,
myState->output_cid,
myState->hi_options,
myState->bistate);
@@ -522,7 +522,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0f08245..d46e2e6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4601,7 +4602,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate);
ResetExprContext(econtext);
@@ -4625,7 +4626,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8634473..d2438ce 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2411,17 +2411,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3061,9 +3065,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3079,11 +3084,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, &tuple,
estate->es_output_cid,
lockmode, LockWaitBlock,
false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3115,7 +3120,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3161,6 +3167,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3179,17 +3186,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3898,8 +3905,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -3974,10 +3981,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -3991,10 +3997,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 2946a0e..8bf0f61 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1887,7 +1887,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1967,7 +1967,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2014,7 +2014,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2473,7 +2473,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2490,7 +2491,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2521,7 +2524,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2554,14 +2557,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2569,12 +2572,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2588,8 +2591,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2610,7 +2613,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2639,8 +2643,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ t_data.cid >= estate->es_output_cid)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2649,7 +2654,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, tuple,
estate->es_output_cid,
lockmode, wait_policy,
false, &buffer, &hufd);
@@ -2688,12 +2693,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2715,10 +2723,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2727,7 +2731,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2736,8 +2740,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2755,7 +2759,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2763,17 +2769,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2819,7 +2827,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2838,7 +2846,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2866,7 +2874,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2897,8 +2905,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2922,11 +2928,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
erm,
datum,
&updated);
- if (copyTuple == NULL)
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2940,23 +2946,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2966,19 +2967,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = erm->relation->rd_stamroutine->tuple_from_datum(datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3144,8 +3138,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index d424074..a1c2319 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,7 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -349,6 +351,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ tuple_data t_data;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
@@ -359,14 +362,15 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
+ t_data = storage_tuple_get_data(rel, tuple, TID);
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(t_data.tid),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +394,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -426,8 +430,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* OK, update the tuple and index entries for it */
simple_heap_update(rel, tid, tuple);
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
+ if (resultRelInfo->ri_NumIndices > 0 && storage_tuple_is_heaponly(rel, tuple))
recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 9389560..f06f34a 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple)(&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
estate->es_output_cid,
lockmode, erm->waitPolicy, true,
&buffer, &hufd);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f7051b3..a5f469a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
+ Buffer buffer;
Relation rel = relinfo->ri_RelationDesc;
- Buffer buffer;
- HeapTupleData tuple;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate,
TupleTableSlot *result = NULL;
/*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
- /*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -371,19 +371,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where the
+ * plan tree can return a tuple extracted literally from some table with
+ * the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -401,9 +413,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -415,9 +424,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -433,14 +439,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -460,7 +464,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -551,24 +556,24 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
HEAP_INSERT_SPECULATIVE,
NULL);
/* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, true, &specConflict,
arbiterIndexes);
/* adjust the tuple's state accordingly */
if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
+ storage_finish_speculative(resultRelationDesc, slot);
else
- heap_abort_speculative(resultRelationDesc, tuple);
+ storage_abort_speculative(resultRelationDesc, slot);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -596,17 +601,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL,
arbiterIndexes);
}
@@ -616,11 +618,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -671,7 +673,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -716,8 +718,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -743,8 +743,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -758,7 +760,7 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
+ result = storage_delete(resultRelationDesc, tupleid,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -858,7 +860,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -872,20 +874,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -894,7 +895,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -931,14 +932,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1003,14 +1004,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1020,7 +1021,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1056,7 +1057,7 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
+ result = storage_update(resultRelationDesc, tupleid, slot,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -1148,8 +1149,8 @@ lreplace:;
*
* If it's a HOT update, we mustn't insert new index entries.
*/
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && !storage_tuple_is_heaponly(resultRelationDesc, tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL, NIL);
}
@@ -1206,11 +1207,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1221,10 +1223,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1249,7 +1249,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1280,7 +1281,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1308,10 +1311,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1326,7 +1329,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1366,12 +1371,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1552,7 +1559,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7..8a6b217 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -332,12 +333,6 @@ TidNext(TidScanState *node)
numTids = node->tss_NumTids;
/*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
- /*
* Initialize or advance scan position, depending on direction.
*/
bBackward = ScanDirectionIsBackward(direction);
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ //hari
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 083f7d6..52779b7 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -354,7 +355,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -389,7 +390,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index b2132e7..b40d2d5 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -132,40 +132,19 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -178,7 +157,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 1fe15ed..799b4ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000..9502c92
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+extern void storage_finish_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 95fe028..c2e6dc2 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -13,32 +13,13 @@
#include "access/htup.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index aeb363f..0da9ac9 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -189,7 +189,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index f48a603..b2913f2 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,7 +196,7 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +204,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d1565e7..d69414e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -2016,7 +2016,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
1.8.3.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-07 01:53 ` Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Thomas Munro @ 2017-09-07 01:53 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Sep 1, 2017 at 1:51 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:
> Here I attached new set of patches that are rebased to the latest master.
Hi Haribabu,
Could you please post a new rebased version?
0007-Scan-functions-are-added-to-storage-AM.patch conflicts with
recent changes.
Thanks!
--
Thomas Munro
http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
@ 2017-09-09 03:23 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-09-09 03:23 UTC (permalink / raw)
To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Sep 7, 2017 at 11:53 AM, Thomas Munro <thomas.munro@enterprisedb.com
> wrote:
> On Fri, Sep 1, 2017 at 1:51 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
> > Here I attached new set of patches that are rebased to the latest master.
>
> Hi Haribabu,
>
> Could you please post a new rebased version?
> 0007-Scan-functions-are-added-to-storage-AM.patch conflicts with
> recent changes.
>
Thanks for checking the patch.
I rebased the patch to the latest master and also fixed the duplicate OID
and some slot fixes. Updated patches are attached.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (178.0K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/3-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 9da4ba367a4eb8e7f0722ff7c647c1a8f491b34c Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:45:04 +1000
Subject: [PATCH 7/7] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1504 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1299 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 2 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 7 +-
src/backend/access/index/indexam.c | 3 +-
src/backend/access/nbtree/nbtinsert.c | 5 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 27 +-
src/backend/catalog/partition.c | 6 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 13 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 16 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 11 +-
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 12 +-
src/backend/executor/nodeIndexonlyscan.c | 4 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 21 +-
src/backend/executor/nodeSeqscan.c | 39 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 16 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 30 +-
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 42 +-
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 10 +-
src/include/executor/tqueue.h | 2 +-
src/include/funcapi.h | 2 +-
56 files changed, 1924 insertions(+), 1734 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 5f076ef..063e079 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index f7b68a8..eb33b26 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f211..b64fec8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,603 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
@@ -1186,317 +443,96 @@ relation_close(Relation relation, LOCKMODE lockmode)
UnlockRelationId(&relid, lockmode);
}
-
-/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
-}
-
+
/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
/* ----------------
@@ -1550,384 +586,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return &(scan->rs_ctup);
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- *
- * Note: if you change the criterion here for what is "dead", fix the
- * planner's get_actual_variable_range() function to match.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
-
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
*
@@ -4762,32 +3420,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index a0e3272..12a8f56 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1666,6 +1666,1094 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
/*
* heap_fetch - retrieve tuple with given tid
@@ -1818,9 +2906,210 @@ heap_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
/* ----------------------------------------------------------------
* storage AM support routines for heapam
@@ -3793,6 +5082,16 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 8fba61c..a475a85 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,7 +409,7 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
state->rs_cutoff_multi);
/*
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index d1d7364..76b94dc 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot*
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2..db5c93b 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,7 +395,7 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
nkeys, key,
true, false);
sysscan->iscan = NULL;
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255..349a127 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,7 +606,7 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
scan->xs_cbuf,
scan->xs_snapshot,
&scan->xs_ctup,
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fc..0e25e9a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,7 +326,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
&all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4..975cd5b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
@@ -573,18 +574,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -592,7 +593,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -903,25 +904,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a..d2a8a06 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0240df7..68c46f7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,7 +2280,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -2288,7 +2289,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
true); /* syncscan OK */
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,7 +3005,7 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -3014,7 +3015,7 @@ validate_index_heapscan(Relation heapRelation,
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 73eff17..b8137f2 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -986,7 +986,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -995,7 +995,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1011,7 +1011,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..1d048e6 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..517e310 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8..145e3c1 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71..a51f2e4 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc..e0f6973 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e5..6727d15 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c81ddf5..00e71e3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2028,10 +2028,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2043,7 +2043,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539..39850b1 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b61aaac..46bc3da 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1948,8 +1949,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1989,7 +1990,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bca2a4c..c0320ec 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4540,7 +4540,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4548,7 +4548,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4661,7 +4661,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5064,9 +5064,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5078,7 +5078,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8243,7 +8243,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8251,7 +8251,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8266,7 +8266,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8321,9 +8321,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8352,7 +8352,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10802,8 +10802,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10862,7 +10862,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b..cdfa8ff 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d2438ce..fbbdbc2 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16ae..c07f508 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2315,8 +2316,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2345,7 +2346,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2711,8 +2712,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2755,7 +2756,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index faa1812..e24ac9f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -447,9 +448,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -469,7 +470,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
}
@@ -1121,9 +1122,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1160,7 +1161,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5..6ade9df 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189f..5e9daea 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index e2bcf90..6ec17bd 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,20 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +313,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b7a2cbc..0a5098d 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -681,7 +681,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -765,7 +765,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1085,7 +1085,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1093,7 +1093,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 42a4ca9..79b74ee 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0ae5873..d94169c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3097,7 +3097,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3212,7 +3212,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 60a6cb0..7921025 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
@@ -400,7 +401,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
@@ -685,7 +686,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -764,7 +765,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -865,7 +866,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
estate->es_snapshot,
0,
NULL);
@@ -1023,5 +1024,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 02f6c81..abec3a9 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 022d75b..5f4a294 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -252,7 +252,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -295,7 +295,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -303,7 +303,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index d20d466..ef9d9f1 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,7 +44,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -53,7 +53,7 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
@@ -399,7 +399,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -617,7 +617,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -689,12 +689,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5351cb8..f770bc4 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,7 +186,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
+ * provided in either StorageTuple or IndexTuple format. Conceivably an
* index AM might fill both fields, in which case we prefer the heap
* format, since it's probably a bit cheaper to fill a slot from.
*/
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 638b17b..7330ff9 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1..04f85e5 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+ HeapScanDesc scan); //hari
/* ----------------------------------------------------------------
* Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -244,7 +245,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,7 +350,7 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
scanstate->ss.ps.state->es_snapshot,
0, NULL,
scanstate->use_bulkread,
@@ -358,7 +359,7 @@ tablesample_init(SampleScanState *scanstate)
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
@@ -376,7 +377,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +555,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index d4ac939..839d3a6 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,7 +69,7 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
estate->es_snapshot,
0, NULL);
node->ss.ss_currentScanDesc = scandesc;
@@ -78,26 +78,7 @@ SeqNext(SeqScanState *node)
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,12 +251,12 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
+ storage_rescan(scan, /* scan desc */
NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
@@ -322,7 +303,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -353,5 +334,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 80be460..d55a752 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2091,7 +2091,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2159,7 +2159,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index afe231f..418c2a6 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -854,7 +854,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -962,7 +962,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1683,7 +1683,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1714,8 +1714,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 81964d7..81d0adc 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -192,12 +192,12 @@ static void TQSendRecordInfo(TQueueDestReceiver *tqueue, int32 typmod,
TupleDesc tupledesc);
static void TupleQueueHandleControlMessage(TupleQueueReader *reader,
Size nbytes, char *data);
-static HeapTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
+static StorageTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes, HeapTupleHeader data);
-static HeapTuple TQRemapTuple(TupleQueueReader *reader,
+static StorageTuple TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple);
+ StorageTuple tuple);
static Datum TQRemap(TupleQueueReader *reader, TupleRemapInfo *remapinfo,
Datum value, bool *changed);
static Datum TQRemapArray(TupleQueueReader *reader, ArrayRemapInfo *remapinfo,
@@ -682,7 +682,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
shm_mq_result result;
@@ -737,7 +737,7 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
/*
* Handle a data message - that is, a tuple - from the remote side.
*/
-static HeapTuple
+static StorageTuple
TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes,
HeapTupleHeader data)
@@ -766,11 +766,11 @@ TupleQueueHandleDataMessage(TupleQueueReader *reader,
/*
* Copy the given tuple, remapping any transient typmods contained in it.
*/
-static HeapTuple
+static StorageTuple
TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple)
+ StorageTuple tuple)
{
Datum *values;
bool *isnull;
@@ -1128,7 +1128,7 @@ TupleQueueHandleControlMessage(TupleQueueReader *reader, Size nbytes,
static TupleRemapInfo *
BuildTupleRemapInfo(Oid typid, MemoryContext mycontext)
{
- HeapTuple tup;
+ StorageTuple tup;
Form_pg_type typ;
/* This is recursive, so it could be driven to stack overflow. */
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..fec203d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1882,9 +1882,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1911,7 +1911,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2042,13 +2042,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2134,7 +2134,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2142,8 +2142,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2189,7 +2189,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302..74113a7 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 6c89442..fca56d4 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index d03984a..d71d673 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index eb6960d..82c042a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1208,10 +1209,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index cdd45ef..4cddd73 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,40 +106,16 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
+
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
@@ -147,8 +123,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -158,8 +132,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 799b4ed..66a96d7 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 9502c92..507af71 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,6 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -36,6 +37,34 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -44,6 +73,15 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
@@ -72,10 +110,6 @@ extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
-extern void storage_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid);
-
extern void storage_sync(Relation rel);
#endif
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d894..7f9bef1 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e..d466c99 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index a717ac6..4156767 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -27,7 +27,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle,
TupleDesc tupledesc);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2a..ab0e091 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.7.4.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.6K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/4-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From 1a7186e8d05b2efd73d317d7db8f3091bd48f9bf Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/7] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 4 ++++
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa..33079c1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818..d4ac340 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5172,16 +5174,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be79353..0a7e0a3 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b..2c3e33c 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
@@ -83,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56..e3c9c19 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3870,6 +3870,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 336 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 337 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452..ea352fa 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7..3bc9607 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1711,11 +1711,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 2945966..f1f58a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1153,15 +1153,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.7.4.windows.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.3K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/5-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From 1a9c4a4a66e75fdddbc88782d4abbf81fbea3a28 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/7] Storage AM API hooks and related functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 237 +++++++++++++++++++++++++++++++
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
8 files changed, 427 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496..02a3909 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000..88827e7
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000..def2029
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine *HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68..6459435 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000..95fe028
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,237 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*scan_end_hook) (HeapScanDesc scan);
+
+
+typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*tuple_insert_hook) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*tuple_delete_hook) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*tuple_update_hook) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*tuple_fetch_hook) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*tuple_lock_hook) (Relation relation,
+ ItemPointer tid,
+ StorageTuple *tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*multi_insert_hook) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*tuple_freeze_hook) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*tuple_get_latest_tid_hook) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data (*get_tuple_data_hook) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple (*tuple_from_datum_hook) (Datum data, Oid tableoid);
+
+typedef bool (*tuple_is_heaponly_hook) (StorageTuple tuple);
+
+typedef void (*slot_store_tuple_hook) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*slot_clear_tuple_hook) (TupleTableSlot *slot);
+typedef Datum (*slot_getattr_hook) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*slot_virtualize_tuple_hook) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*slot_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*slot_min_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*slot_update_tableoid_hook) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*speculative_finish_hook) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*speculative_abort_hook) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*relation_sync_hook) (Relation relation);
+
+typedef bool (*snapshot_satisfies_hook) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*snapshot_satisfies_update_hook) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*snapshot_satisfies_vacuum_hook) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ slot_store_tuple_hook slot_store_tuple;
+ slot_virtualize_tuple_hook slot_virtualize_tuple;
+ slot_clear_tuple_hook slot_clear_tuple;
+ slot_getattr_hook slot_getattr;
+ slot_tuple_hook slot_tuple;
+ slot_min_tuple_hook slot_min_tuple;
+ slot_update_tableoid_hook slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine* (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ scan_begin_hook scan_begin;
+ scan_setlimits_hook scansetlimits;
+ scan_getnext_hook scan_getnext;
+ scan_getnext_slot_hook scan_getnextslot;
+ scan_end_hook scan_end;
+ scan_getpage_hook scan_getpage;
+ scan_rescan_hook scan_rescan;
+ scan_update_snapshot_hook scan_update_snapshot;
+ hot_search_buffer_hook hot_search_buffer; /* heap_hot_search_buffer */
+
+ // heap_sync_function heap_sync; /* heap_sync */
+ /* not implemented */
+ // parallelscan_estimate_function parallelscan_estimate; /* heap_parallelscan_estimate */
+ // parallelscan_initialize_function parallelscan_initialize; /* heap_parallelscan_initialize */
+ // parallelscan_begin_function parallelscan_begin; /* heap_beginscan_parallel */
+
+ /* Operations on physical tuples */
+ tuple_insert_hook tuple_insert; /* heap_insert */
+ tuple_update_hook tuple_update; /* heap_update */
+ tuple_delete_hook tuple_delete; /* heap_delete */
+ tuple_fetch_hook tuple_fetch; /* heap_fetch */
+ tuple_lock_hook tuple_lock; /* heap_lock_tuple */
+ multi_insert_hook multi_insert; /* heap_multi_insert */
+ tuple_freeze_hook tuple_freeze; /* heap_freeze_tuple */
+ tuple_get_latest_tid_hook tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ get_tuple_data_hook get_tuple_data;
+ tuple_is_heaponly_hook tuple_is_heaponly;
+ tuple_from_datum_hook tuple_from_datum;
+
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no need
+ * for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ speculative_finish_hook speculative_finish;
+ speculative_abort_hook speculative_abort;
+
+
+ relation_sync_hook relation_sync; /* heap_sync */
+
+ snapshot_satisfies_hook snapshot_satisfies[END_OF_VISIBILITY];
+ snapshot_satisfies_update_hook snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ snapshot_satisfies_vacuum_hook snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine *GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine *GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine *GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e3c9c19..03cb703 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -549,6 +549,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3..108df9b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -496,6 +496,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 9a3b56e..de75e01 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
2.7.4.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/6-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From 1079b9ffe466449b917996117157979972c2bccd Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/7] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 117 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e3780..40cbc67 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1885,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2027,6 +2109,11 @@ formrdesc(const char *relationName, Oid relationReltype,
RelationInitPhysicalAddr(relation);
/*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
+ /*
* initialize the rel-has-index flag, using hardwired knowledge
*/
if (IsBootstrapProcessingMode())
@@ -2354,6 +2441,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3457,13 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3985,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert (relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5715,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..82d8a53 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -161,6 +161,12 @@ typedef struct RelationData
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
+ /*
* index access support info (used only for an index relation)
*
* Note: only default support procs for each opclass are cached, namely
@@ -428,6 +434,12 @@ typedef struct ViewOptions
#define RelationGetDescr(relation) ((relation)->rd_att)
/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
+/*
* RelationGetRelationName
* Returns the rel's name.
*
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cef..03d996f 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.7.4.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (145.7K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/7-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From 4f0abf567c359ce0003410e246ba098fff09d3fe Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:38:28 +1000
Subject: [PATCH 4/7] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 10 +-
contrib/pgrowlocks/pgrowlocks.c | 3 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 23 +-
src/backend/access/heap/heapam_common.c | 162 +++
src/backend/access/heap/heapam_storage.c | 1622 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 4 +-
src/backend/commands/analyze.c | 2 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 7 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1809 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/access/htup.h | 3 +-
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 66 +-
29 files changed, 1964 insertions(+), 1907 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575..10d47cd 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -51,7 +51,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +656,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +681,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +739,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65..5f076ef 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,7 +150,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
GetCurrentCommandId(false),
scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf0613..284eabc 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7a91cc3..f7b68a8 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909..e6bc18e 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f038..c21d6f8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -1950,7 +1953,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2097,7 +2100,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2271,7 +2274,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3095,7 +3098,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3206,7 +3209,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3666,7 +3669,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3847,7 +3850,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4598,7 +4601,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000..502f6db
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
+
+
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 88827e7..1bd4bfa 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,6 +48,1617 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
+ */
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
+ Buffer buffer)
+{
+ return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+ != HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except for known-committed
+ * XIDs which could not be ours anyway.
+ */
+static bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
@@ -54,5 +1666,15 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
+ amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
+ amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
+ amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
+ amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
+ amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] = HeapTupleSatisfiesHistoricMVCC;
+ amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] = HeapTupleSatisfiesNonVacuumable;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac..7425068 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index bd560e4..191f088 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da0..01321a2 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f03..0240df7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,7 +2359,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 08fc18e..2611cca 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1069,7 +1069,7 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
OldestXmin,
targbuffer))
{
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e..dbcc5bc 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 45b1859..dccda5a 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -975,7 +975,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2140,7 +2140,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index f7e55e0..60a6cb0 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -428,7 +428,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index bd84778..e609eff 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1307,7 +1308,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a83..6a118d1 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index fba57a0..095d1ed 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 6a6d9d6..22948cb 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6..5a6d216 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa..f17b1c5 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 08a08c8..1f96913 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2040,7 +2040,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2137,7 +2137,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index bbac408..0000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1809 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-/* local functions */
-static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleSatisfiesNonVacuumable
- *
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
- *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
- */
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
- != HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except for known-committed
- * XIDs which could not be ours anyway.
- */
-static bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000..ff63cf3
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6459435..5b0068a 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -29,11 +29,12 @@ typedef MinimalTupleData *MinimalTuple;
typedef enum tuple_visibility_type
{
MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
- SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
END_OF_VISIBILITY
} tuple_visibility_type;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc..b8b823b 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf51977..5752ee4 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -52,7 +52,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index de75e01..67a8b1e 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,48 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
-
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies[(snapshot)->visibility_type]) (tuple, snapshot, buffer))
/*
* We don't provide a static SnapshotDirty variable because it would be
@@ -91,14 +52,14 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -106,8 +67,19 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
2.7.4.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (60.2K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/8-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From a6f0054aaaf30da1cb3370b36e35072ae7aad972 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 17:24:35 +1000
Subject: [PATCH 5/7] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 1 +
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 15 +-
src/backend/executor/execExprInterp.c | 26 +-
src/backend/executor/execReplication.c | 92 ++-----
src/backend/executor/execTuples.c | 269 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 644 insertions(+), 575 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528..5ed0f15 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1022,111 +1023,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
-/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
* It is functionally equivalent to heap_getattr, but fetches of
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 502f6db..6cb6c5b 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -159,4 +159,408 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *)palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple)tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple)tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine*
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 1bd4bfa..7d7ac75 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1665,6 +1665,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->slot_storageam = heapam_storage_slot_handler;
amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cfa3f05..8456bfd 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2695,7 +2695,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210c..a0ec444 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376..b440740 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index da0850b..8634473 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2348,7 +2348,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2429,7 +2429,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2777,7 +2777,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2819,7 +2819,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2928,7 +2928,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3960,14 +3960,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index bd8a15d..4775738 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -503,12 +503,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+ //hari Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(innerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -519,12 +519,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
-
+ Assert(outerslot->tts_storage != NULL);
+ //hari Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
+
+ *op->resvalue = slot_getattr(outerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -535,12 +535,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+ //hari Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(scanslot,
+ attnum,
op->resnull);
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fbb8108..d424074 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,7 +226,8 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ TupleTableSlot *scanslot;
+ HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
TransactionId xwait;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d..b7a2cbc 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -206,6 +206,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
}
/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert (slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
+/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,18 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
-
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +459,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +540,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +549,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +568,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +598,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +645,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +653,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +664,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +697,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +717,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,19 +728,11 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
/*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
* mustn't leave any dangling pass-by-reference datums in tts_values.
@@ -768,17 +741,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6..02f6c81 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e609eff..7bf8bef 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -403,7 +403,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -416,7 +416,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -434,7 +434,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -743,7 +743,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -894,7 +894,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -955,7 +955,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -974,7 +974,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -988,7 +988,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1004,7 +1004,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1120,7 +1120,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 6afcd1a..81964d7 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -305,7 +305,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
}
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 041f387..47dc627 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -724,9 +724,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3..1fe15ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index fa04a63..9539d67 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -21,6 +21,19 @@
#include "storage/bufpage.h"
/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
+/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
* a tuple, plus the size of the null-values bitmap (at 1 bit per column),
@@ -653,7 +666,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce..84dc293 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.7.4.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (246.1K, ../../CAJrrPGc_NjN-+jxNpvMEkAszSCDw=GfWSisTjkcwnzq9_0PK1A@mail.gmail.com/9-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From 955ec4cdf0b0db0df97f183d394481f72825f4d0 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Thu, 7 Sep 2017 14:48:24 +1000
Subject: [PATCH 6/7] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM.
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2737 ++++--------------------------
src/backend/access/heap/heapam_storage.c | 2153 ++++++++++++++++++++++-
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 306 ++++
src/backend/access/heap/tuptoaster.c | 8 +-
src/backend/commands/copy.c | 29 +-
src/backend/commands/createas.c | 18 +-
src/backend/commands/matview.c | 10 +-
src/backend/commands/tablecmds.c | 5 +-
src/backend/commands/trigger.c | 43 +-
src/backend/executor/execMain.c | 120 +-
src/backend/executor/execReplication.c | 35 +-
src/backend/executor/nodeLockRows.c | 39 +-
src/backend/executor/nodeModifyTable.c | 177 +-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/htup_details.h | 1 +
src/include/access/storageam.h | 81 +
src/include/access/storageamapi.h | 21 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 6 +-
src/include/nodes/execnodes.h | 4 +-
26 files changed, 3290 insertions(+), 2717 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 5ed0f15..714c4d8 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -686,6 +686,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
}
/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
+/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
* which are of the length indicated by tupleDescriptor->natts
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e..162736f 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index c21d6f8..d20f211 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1838,158 +1745,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
}
/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
-/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
* On entry, *tid is the TID of a tuple (either a simple tuple, or the root
@@ -2172,130 +1927,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -2313,7 +1944,7 @@ heap_get_latest_tid(Relation relation,
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2596,7 +2227,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2664,412 +2295,110 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
-
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ Assert(ItemPointerIsValid(tid));
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
*/
if (PageIsAllVisible(page))
visibilitymap_pin(relation, block, &vmbuffer);
@@ -4504,7 +3833,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4522,724 +3851,34 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
-
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
+ if (*have_tuple_lock)
+ return true;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode))
@@ -5272,7 +3911,7 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
* window, but it's still possible to end up creating an unnecessary
* MultiXactId. Fortunately this is harmless.
*/
-static void
+void
compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
uint16 old_infomask2, TransactionId add_to_xmax,
LockTupleMode mode, bool is_update,
@@ -5319,916 +3958,226 @@ l5:
break;
case LockTupleNoKeyExclusive:
new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
- }
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
-}
-
-/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
- *
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
- *
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
- *
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
- */
-void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
-{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
-
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
-
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
+}
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
-}
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
@@ -6993,7 +4942,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7160,7 +5109,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7182,7 +5131,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7744,7 +5693,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9121,46 +7070,6 @@ heap2_redo(XLogReaderState *record)
}
/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
-/*
* Mask a heap page before performing consistency checks on it.
*/
void
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 7d7ac75..a0e3272 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -50,7 +50,13 @@
/* local functions */
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
/*-------------------------------------------------------------------------
*
* POSTGRES "time qualification" code, ie, tuple visibility rules.
@@ -1660,10 +1666,2149 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
-Datum
-heapam_storage_handler(PG_FUNCTION_ARGS)
+
+/*
+ * heap_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
{
- StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple->t_self));
+ tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple->t_len = ItemIdGetLength(lp);
+ tuple->t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is completely
+ * bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ /*Assert(slot->tts_speculativeToken != 0); This needs some update in toast */
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTupleData mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ priorXmax))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup.t_data->t_infomask;
+ old_infomask2 = mytup.t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup.t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup.t_data->t_infomask |= new_infomask;
+ mytup.t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data)HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data)HeapTupleHeaderGetUpdateXid(((HeapTuple)tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data)HeapTupleHeaderGetCmin(((HeapTuple)tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data)((HeapTuple)tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data)((HeapTuple)tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static bool
+heapam_tuple_is_heaopnly(StorageTuple tuple)
+{
+ return HeapTupleIsHeapOnly((HeapTuple)tuple);
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_finish = heapam_finish_speculative;
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_is_heaponly = heapam_tuple_is_heaopnly;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
amroutine->slot_storageam = heapam_storage_slot_handler;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 191f088..8fba61c 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000..d1d7364
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,306 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid,
+ options, bistate);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, cid,
+ crosscheck, wait, hufd, lockmode);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+
+/*
+ * storage_finish_speculative - mark speculative insertion as successful
+ *
+ * To successfully finish a speculative insertion we have to clear speculative
+ * token from tuple. To do so the t_ctid field, which will contain a
+ * speculative token value, is modified in place to point to the tuple itself,
+ * which is characteristic of a newly inserted ordinary tuple.
+ *
+ * NB: It is not ok to commit without either finishing or aborting a
+ * speculative insertion. We could treat speculative tuples of committed
+ * transactions implicitly as completed, but then we would have to be prepared
+ * to deal with speculative tokens on committed tuples. That wouldn't be
+ * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
+ * but clearing the token at completion isn't very expensive either.
+ * An explicit confirmation WAL record also makes logical decoding simpler.
+ */
+void
+storage_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_finish(relation, slot);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+bool
+storage_tuple_is_heaponly(Relation relation, StorageTuple tuple)
+{
+ return relation->rd_stamroutine->tuple_is_heaponly(tuple);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1da..d766a6e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,12 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8456bfd..c81ddf5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2694,8 +2695,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2758,19 +2757,18 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
+ &(slot->tts_tid),
estate,
false,
NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2866,7 +2864,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2899,12 +2897,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2923,7 +2921,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2940,8 +2938,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index a0ec444..d119149 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,24 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ slot->tts_tupleOid = InvalidOid;
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +619,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740..6102481 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,16 +492,15 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
- heap_insert(myState->transientrel,
- tuple,
+ storage_insert(myState->transientrel,
+ slot,
myState->output_cid,
myState->hi_options,
myState->bistate);
@@ -522,7 +522,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d2167ed..bca2a4c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4652,7 +4653,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate);
ResetExprContext(econtext);
@@ -4676,7 +4677,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8634473..d2438ce 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2411,17 +2411,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3061,9 +3065,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3079,11 +3084,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, &tuple,
estate->es_output_cid,
lockmode, LockWaitBlock,
false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3115,7 +3120,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3161,6 +3167,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3179,17 +3186,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3898,8 +3905,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -3974,10 +3981,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -3991,10 +3997,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4b594d4..a7127e4 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1894,7 +1894,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1974,7 +1974,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2021,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2480,7 +2480,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2497,7 +2498,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2528,7 +2531,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2561,14 +2564,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2576,12 +2579,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2595,8 +2598,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2617,7 +2620,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2646,17 +2650,20 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, tuple,
estate->es_output_cid,
lockmode, wait_policy,
false, &buffer, &hufd);
@@ -2695,12 +2702,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2722,10 +2732,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2734,7 +2740,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2743,8 +2749,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2762,7 +2768,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2770,17 +2778,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2826,7 +2836,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2845,7 +2855,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2873,7 +2883,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2904,8 +2914,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2929,11 +2937,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
erm,
datum,
&updated);
- if (copyTuple == NULL)
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2947,23 +2955,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2973,19 +2976,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3151,8 +3147,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index d424074..e2bcf90 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,7 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -349,6 +351,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ tuple_data t_data;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
@@ -359,14 +362,15 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
+ t_data = storage_tuple_get_data(rel, tuple, TID);
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(t_data.tid),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +394,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -426,8 +430,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* OK, update the tuple and index entries for it */
simple_heap_update(rel, tid, tuple);
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
+ if (resultRelInfo->ri_NumIndices > 0 && !storage_tuple_is_heaponly(rel, tuple))
recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 9389560..f06f34a 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple)(&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
estate->es_output_cid,
lockmode, erm->waitPolicy, true,
&buffer, &hufd);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 7bf8bef..e8e818c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
+ Buffer buffer;
Relation rel = relinfo->ri_RelationDesc;
- Buffer buffer;
- HeapTupleData tuple;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate,
TupleTableSlot *result = NULL;
/*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
- /*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -371,19 +371,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where the
+ * plan tree can return a tuple extracted literally from some table with
+ * the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -401,9 +413,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -415,9 +424,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -433,14 +439,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -460,7 +464,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -551,24 +556,24 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
HEAP_INSERT_SPECULATIVE,
NULL);
/* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, true, &specConflict,
arbiterIndexes);
/* adjust the tuple's state accordingly */
if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
+ storage_finish_speculative(resultRelationDesc, slot);
else
- heap_abort_speculative(resultRelationDesc, tuple);
+ storage_abort_speculative(resultRelationDesc, slot);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -596,17 +601,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL,
arbiterIndexes);
}
@@ -616,11 +618,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -671,7 +673,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -716,8 +718,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -743,8 +743,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -758,7 +760,7 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
+ result = storage_delete(resultRelationDesc, tupleid,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -858,7 +860,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -872,20 +874,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -894,7 +895,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -931,14 +932,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1003,14 +1004,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1020,7 +1021,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1056,7 +1057,7 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
+ result = storage_update(resultRelationDesc, tupleid, slot,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -1148,8 +1149,8 @@ lreplace:;
*
* If it's a HOT update, we mustn't insert new index entries.
*/
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && !storage_tuple_is_heaponly(resultRelationDesc, tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL, NIL);
}
@@ -1206,11 +1207,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1221,10 +1223,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1249,7 +1249,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1280,7 +1281,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1308,10 +1311,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1326,7 +1329,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1366,12 +1371,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1552,7 +1559,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7..8a6b217 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -332,12 +333,6 @@ TidNext(TidScanState *node)
numTids = node->tss_NumTids;
/*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
- /*
* Initialize or advance scan position, depending on direction.
*/
bBackward = ScanDirectionIsBackward(direction);
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ //hari
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 083f7d6..52779b7 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -354,7 +355,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -389,7 +390,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024..cdd45ef 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -133,40 +133,19 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -179,7 +158,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 1fe15ed..799b4ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 9539d67..16dfb3e 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -811,6 +811,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000..9502c92
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+extern void storage_finish_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 95fe028..c2e6dc2 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -13,32 +13,13 @@
#include "access/htup.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index aeb363f..0da9ac9 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -189,7 +189,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7708818..8704b7b 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,7 +196,7 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +204,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 90a60ab..1ed1864 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -2021,7 +2021,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
2.7.4.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-12 05:52 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-09-12 05:52 UTC (permalink / raw)
To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Sep 9, 2017 at 1:23 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> I rebased the patch to the latest master and also fixed the duplicate OID
> and some slot fixes. Updated patches are attached.
>
While analyzing the removal of HeapScanDesc usage other than heap
modules, The mostly used member is "*rs_cbuf*" for the purpose of locking
the buffer, visibility checks and marking it as dirty. The buffer is
tightly integrated
with the visibility. Buffer may not be required for some storage routines
where
the data is always in the memory and etc.
I found the references of its structure members in the following
places. I feel other than the following 4 parameters, rest of them needs to
be
moved into their corresponding storage routines.
* Relation rs_rd; /* heap relation descriptor */*
* Snapshot rs_snapshot; /* snapshot to see */*
* int rs_nkeys; /* number of scan keys */*
* ScanKey rs_key; /* array of scan key descriptors */*
But currently I am treating the "*rs_cbuf" *also a needed member and also
expecting all storage routines will be provide it. Or we may need a another
approach to mark the buffer as dirty.
Suggestions?
Following are the rest of the parameters that are used
outside the heap.
* BlockNumber rs_nblocks; /* total number of blocks in rel */*
pgstattuple.c, tsm_system_rows.c, tsm_system_time.c, system.c
nodeBitmapheapscan.c nodesamplescan.c,
*Mostly for the purpose of checking the number of blocks in a rel.*
* BufferAccessStrategy rs_strategy; /* access strategy for reads */*
pgstattuple.c
* bool rs_pageatatime; /* verify visibility page-at-a-time? */*
* BlockNumber rs_startblock; /* block # to start at */*
* bool rs_syncscan; /* report location to syncscan logic? */*
* bool rs_inited; /* false = scan not init'd yet */*
nodesamplescan.c
* HeapTupleData rs_ctup; /* current tuple in scan, if any */*
*genam.c, nodeBitmapHeapscan.c, nodesamplescan.c*
*Used for retrieving the last scanned tuple.*
* BlockNumber rs_cblock; /* current block # in scan, if any */*
*index.c, nodesamplescan.c*
* Buffer rs_cbuf; /* current buffer in scan, if any */*
*pgrowlocks.c, pgstattuple.c, genam.c, index.c, cluster.c,*
*tablecmds.c, nodeBitmapHeapscan.c, nodesamplescan.c*
*Mostly used for Locking the Buffer.*
* ParallelHeapScanDesc rs_parallel; /* parallel scan information */*
*nodeseqscan.c*
* int rs_cindex; /* current tuple's index in vistuples */*
*nodeBitmapHeapScan.c*
* int rs_ntuples; /* number of visible tuples on page */*
* OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */*
*tsm_system_rows.c, nodeBitmapHeapscan.c, nodesamplescan.c*
*Used for retrieve the offsets mainly Bitmap and sample scans.*
I think rest of the above parameters usage other than heap can be changed
once the Bitmap and Sample scans are modified to use the storage routines
while returning the tuple instead of their own implementations. I feel these
scans are the major users of the rest of the parameters. This approach may
need to some more API's to get rid of Bitmap and sample scan's own
implementation.
suggestions?
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-14 05:17 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-09-14 05:17 UTC (permalink / raw)
To: Thomas Munro <thomas.munro@enterprisedb.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Sep 12, 2017 at 3:52 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> * int rs_ntuples; /* number of visible tuples on page */*
> * OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */*
>
> *tsm_system_rows.c, nodeBitmapHeapscan.c, nodesamplescan.c*
> *Used for retrieve the offsets mainly Bitmap and sample scans.*
>
> I think rest of the above parameters usage other than heap can be changed
> once the Bitmap and Sample scans are modified to use the storage routines
> while returning the tuple instead of their own implementations. I feel
> these
> scans are the major users of the rest of the parameters. This approach may
> need to some more API's to get rid of Bitmap and sample scan's own
> implementation.
>
Instead of modifying the Bitmap Heap and Sample scan's to avoid referring
the internal members of the HeapScanDesc, I divided the HeapScanDesc
into two parts.
1. StorageScanDesc
2. HeapPageScanDesc
The StorageScanDesc contains the minimal information that is required
outside
the Storage routine and this must be provided by all storage routines. This
structure contains minimal information such as relation, snapshot, buffer
and
etc.
The HeapPageScanDesc contains other extra information that is required for
Bitmap Heap and Sample scans to work. This structure contains the
information
of blocks, visible offsets and etc. Currently this structure is used only
in
Bitmap Heap and Sample scan and it's supported contrib modules, except
the pgstattuple module. The pgstattuple needs some additional changes.
By adding additional storage API to return HeapPageScanDesc as it required
by the Bitmap Heap and Sample scan's and this API is called only in these
two scan's. And also these scan methods are choosen by the planner only
when the storage routine supports to returning of HeapPageScanDesc API.
Currently Implemented the planner support only for Bitmap, yet to do it
for Sample scan.
With the above approach, I removed all the references of HeapScanDesc
outside the heap. The changes of this approach is available in the
0008-Remove-HeapScanDesc-usage-outside-heap.patch
Suggestions/comments with the above approach.
Because of a recent commit in the master, there was an OID conflict with
the other patches. Rebased patches are attached.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0008-Remove-HeapScanDesc-usage-outside-heap.patch (90.5K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/3-0008-Remove-HeapScanDesc-usage-outside-heap.patch)
download | inline diff:
From cd44aebb658950103342d5a09d62a29db90326ff Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Thu, 14 Sep 2017 12:34:30 +1000
Subject: [PATCH 8/8] Remove HeapScanDesc usage outside heap
HeapScanDesc is divided into two scan descriptors.
StorageScanDesc and HeapPageScanDesc.
StorageScanDesc has common members that are should
be available across all the storage routines and
HeapPageScanDesc is avaiable only for the storage
routine that supports Heap storage with page format.
The HeapPageScanDesc is used internally by the heapam
storage routine and also this is exposed to Bitmap Heap
and Sample scan's as they depend on the Heap page format.
while generating the Bitmap Heap and Sample scan's,
the planner now checks whether the storage routine
supports returning HeapPageScanDesc or not? Based on
this decision, the planner plans above two plans.
---
contrib/pgrowlocks/pgrowlocks.c | 4 +-
contrib/pgstattuple/pgstattuple.c | 10 +-
contrib/tsm_system_rows/tsm_system_rows.c | 18 +-
contrib/tsm_system_time/tsm_system_time.c | 8 +-
src/backend/access/heap/heapam.c | 37 +--
src/backend/access/heap/heapam_storage.c | 417 ++++++++++++++++-------------
src/backend/access/heap/storageam.c | 51 +++-
src/backend/access/index/genam.c | 4 +-
src/backend/access/tablesample/system.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 4 +-
src/backend/catalog/index.c | 8 +-
src/backend/catalog/partition.c | 2 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/cluster.c | 4 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 10 +-
src/backend/commands/tablespace.c | 10 +-
src/backend/commands/typecmds.c | 4 +-
src/backend/commands/vacuum.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 67 +++--
src/backend/executor/nodeSamplescan.c | 51 ++--
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/optimizer/util/plancat.c | 3 +-
src/backend/postmaster/autovacuum.c | 4 +-
src/backend/postmaster/pgstat.c | 2 +-
src/backend/replication/logical/launcher.c | 2 +-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/utils/init/postinit.c | 2 +-
src/include/access/heapam.h | 4 +-
src/include/access/relscan.h | 47 ++--
src/include/access/storageam.h | 32 +--
src/include/access/storageamapi.h | 23 +-
src/include/nodes/execnodes.h | 5 +-
39 files changed, 488 insertions(+), 380 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 063e079..55d2e09 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -56,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -71,7 +71,7 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index eb33b26..6d9a07a 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -328,7 +329,8 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
- nblocks = scan->rs_nblocks; /* # blocks to be scanned */
+ pagescan = storageam_get_heappagescandesc(scan);
+ nblocks = pagescan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
@@ -364,7 +366,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -377,7 +379,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458e..1412029 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -221,14 +221,14 @@ system_rows_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -258,7 +258,7 @@ system_rows_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -291,7 +291,7 @@ system_rows_nextsampletuple(SampleScanState *node,
*/
/* We rely on the data accumulated in pagemode access */
- Assert(scan->rs_pageatatime);
+ Assert(pagescan->rs_pageatatime);
for (;;)
{
/* Advance to next possible offset on page */
@@ -308,7 +308,7 @@ system_rows_nextsampletuple(SampleScanState *node,
}
/* Found a candidate? */
- if (SampleOffsetVisible(tupoffset, scan))
+ if (SampleOffsetVisible(tupoffset, pagescan))
{
sampler->donetuples++;
break;
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025..aa72522 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
instr_time cur_time;
/* First call within scan? */
@@ -232,14 +232,14 @@ system_time_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -275,7 +275,7 @@ system_time_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b64fec8..9b5f24a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -107,8 +107,9 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_mo
* which tuples on the page are visible.
*/
void
-heapgetpage(HeapScanDesc scan, BlockNumber page)
+heapgetpage(StorageScanDesc sscan, BlockNumber page)
{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
Buffer buffer;
Snapshot snapshot;
Page dp;
@@ -118,13 +119,13 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
ItemId lpp;
bool all_visible;
- Assert(page < scan->rs_nblocks);
+ Assert(page < scan->rs_pagescan.rs_nblocks);
/* release previous scan buffer, if any */
- if (BufferIsValid(scan->rs_cbuf))
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
{
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
}
/*
@@ -135,20 +136,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
CHECK_FOR_INTERRUPTS();
/* read page using selected strategy */
- scan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM, page,
- RBM_NORMAL, scan->rs_strategy);
- scan->rs_cblock = page;
+ scan->rs_scan.rs_cbuf = ReadBufferExtended(scan->rs_scan.rs_rd, MAIN_FORKNUM, page,
+ RBM_NORMAL, scan->rs_pagescan.rs_strategy);
+ scan->rs_scan.rs_cblock = page;
- if (!scan->rs_pageatatime)
+ if (!scan->rs_pagescan.rs_pageatatime)
return;
- buffer = scan->rs_cbuf;
- snapshot = scan->rs_snapshot;
+ buffer = scan->rs_scan.rs_cbuf;
+ snapshot = scan->rs_scan.rs_snapshot;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
- heap_page_prune_opt(scan->rs_rd, buffer);
+ heap_page_prune_opt(scan->rs_scan.rs_rd, buffer);
/*
* We must hold share lock on the buffer content while examining tuple
@@ -158,7 +159,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
ntup = 0;
@@ -193,7 +194,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
HeapTupleData loctup;
bool valid;
- loctup.t_tableOid = RelationGetRelid(scan->rs_rd);
+ loctup.t_tableOid = RelationGetRelid(scan->rs_scan.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
@@ -201,20 +202,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, &loctup,
buffer, snapshot);
if (valid)
- scan->rs_vistuples[ntup++] = lineoff;
+ scan->rs_pagescan.rs_vistuples[ntup++] = lineoff;
}
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ scan->rs_pagescan.rs_ntuples = ntup;
}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 12a8f56..a678402 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1711,7 +1711,7 @@ retry:
else
{
SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ sync_startpage = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
goto retry;
}
}
@@ -1753,10 +1753,10 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* starting block number, modulo nblocks.
*/
nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
+ if (nallocated >= scan->rs_pagescan.rs_nblocks)
page = InvalidBlockNumber; /* all blocks have been allocated */
else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_pagescan.rs_nblocks;
/*
* Report scan location. Normally, we report the current page number.
@@ -1765,12 +1765,12 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* doesn't slew backwards. We only report the position at the end of the
* scan once, though: subsequent callers will report nothing.
*/
- if (scan->rs_syncscan)
+ if (scan->rs_pagescan.rs_syncscan)
{
if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ ss_report_location(scan->rs_scan.rs_rd, page);
+ else if (nallocated == scan->rs_pagescan.rs_nblocks)
+ ss_report_location(scan->rs_scan.rs_rd, parallel_scan->phs_startblock);
}
return page;
@@ -1799,9 +1799,9 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* lock that ensures the interesting tuple(s) won't change.)
*/
if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ scan->rs_pagescan.rs_nblocks = scan->rs_parallel->phs_nblocks;
else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+ scan->rs_pagescan.rs_nblocks = RelationGetNumberOfBlocks(scan->rs_scan.rs_rd);
/*
* If the table is large relative to NBuffers, use a bulk-read access
@@ -1815,8 +1815,8 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* Note that heap_parallelscan_initialize has a very similar test; if you
* change this, consider changing that one, too.
*/
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
+ if (!RelationUsesLocalBuffers(scan->rs_scan.rs_rd) &&
+ scan->rs_pagescan.rs_nblocks > NBuffers / 4)
{
allow_strat = scan->rs_allow_strat;
allow_sync = scan->rs_allow_sync;
@@ -1827,20 +1827,20 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (allow_strat)
{
/* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ if (scan->rs_pagescan.rs_strategy == NULL)
+ scan->rs_pagescan.rs_strategy = GetAccessStrategy(BAS_BULKREAD);
}
else
{
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
+ scan->rs_pagescan.rs_strategy = NULL;
}
if (scan->rs_parallel != NULL)
{
/* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ scan->rs_pagescan.rs_syncscan = scan->rs_parallel->phs_syncscan;
}
else if (keep_startblock)
{
@@ -1849,25 +1849,25 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ scan->rs_pagescan.rs_syncscan = (allow_sync && synchronize_seqscans);
}
else if (allow_sync && synchronize_seqscans)
{
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ scan->rs_pagescan.rs_syncscan = true;
+ scan->rs_pagescan.rs_startblock = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
}
else
{
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
+ scan->rs_pagescan.rs_syncscan = false;
+ scan->rs_pagescan.rs_startblock = 0;
}
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
+ scan->rs_pagescan.rs_numblocks = InvalidBlockNumber;
+ scan->rs_scan.rs_inited = false;
scan->rs_ctup.t_data = NULL;
ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
/* page-at-a-time fields are always invalid when not rs_inited */
@@ -1875,7 +1875,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* copy the scan key, if appropriate
*/
if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+ memcpy(scan->rs_scan.rs_key, key, scan->rs_scan.rs_nkeys * sizeof(ScanKeyData));
/*
* Currently, we don't have a stats counter for bitmap heap scans (but the
@@ -1883,7 +1883,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* update stats for tuple fetches there)
*/
if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
+ pgstat_count_heap_scan(scan->rs_scan.rs_rd);
}
@@ -1917,7 +1917,7 @@ heapgettup(HeapScanDesc scan,
ScanKey key)
{
HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
+ Snapshot snapshot = scan->rs_scan.rs_snapshot;
bool backward = ScanDirectionIsBackward(dir);
BlockNumber page;
bool finished;
@@ -1932,14 +1932,14 @@ heapgettup(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -1952,29 +1952,29 @@ heapgettup(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc)scan, page);
lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
/* page and lineoff now reference the physically next tid */
@@ -1985,14 +1985,14 @@ heapgettup(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2003,30 +2003,30 @@ heapgettup(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc)scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineoff = lines; /* final offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
@@ -2042,20 +2042,20 @@ heapgettup(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc)scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2086,21 +2086,21 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
tuple,
snapshot,
- scan->rs_cbuf);
+ scan->rs_scan.rs_cbuf);
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, tuple,
+ scan->rs_scan.rs_cbuf, snapshot);
if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -2125,17 +2125,17 @@ heapgettup(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* advance to next/prior page and detect end of scan
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2146,10 +2146,10 @@ heapgettup(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2163,8 +2163,8 @@ heapgettup(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2172,21 +2172,21 @@ heapgettup(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc)scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber((Page) dp);
linesleft = lines;
if (backward)
@@ -2237,14 +2237,14 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2257,27 +2257,27 @@ heapgettup_pagemode(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc)scan, page);
lineindex = 0;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
+ page = scan->rs_scan.rs_cblock; /* current page */
+ lineindex = scan->rs_pagescan.rs_cindex + 1;
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
/* page and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
@@ -2287,14 +2287,14 @@ heapgettup_pagemode(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2305,32 +2305,32 @@ heapgettup_pagemode(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc)scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineindex = lines - 1;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
- lineindex = scan->rs_cindex - 1;
+ lineindex = scan->rs_pagescan.rs_cindex - 1;
}
/* page and lineindex now reference the previous visible tid */
@@ -2341,20 +2341,20 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc)scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2363,8 +2363,8 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_len = ItemIdGetLength(lpp);
/* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+ Assert(scan->rs_pagescan.rs_cindex < scan->rs_pagescan.rs_ntuples);
+ Assert(lineoff == scan->rs_pagescan.rs_vistuples[scan->rs_pagescan.rs_cindex]);
return;
}
@@ -2377,7 +2377,7 @@ heapgettup_pagemode(HeapScanDesc scan,
{
while (linesleft > 0)
{
- lineoff = scan->rs_vistuples[lineindex];
+ lineoff = scan->rs_pagescan.rs_vistuples[lineindex];
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2392,17 +2392,17 @@ heapgettup_pagemode(HeapScanDesc scan,
{
bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
}
else
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
@@ -2422,10 +2422,10 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2436,10 +2436,10 @@ heapgettup_pagemode(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2453,8 +2453,8 @@ heapgettup_pagemode(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2462,20 +2462,20 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc)scan, page);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
linesleft = lines;
if (backward)
lineindex = lines - 1;
@@ -2485,7 +2485,7 @@ heapgettup_pagemode(HeapScanDesc scan,
}
-static HeapScanDesc
+static StorageScanDesc
heapam_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -2512,12 +2512,12 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
*/
scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
+ scan->rs_scan.rs_rd = relation;
+ scan->rs_scan.rs_snapshot = snapshot;
+ scan->rs_scan.rs_nkeys = nkeys;
scan->rs_bitmapscan = is_bitmapscan;
scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_pagescan.rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_temp_snap = temp_snap;
@@ -2526,7 +2526,7 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
*/
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
/*
* For a seqscan in a serializable transaction, acquire a predicate lock
@@ -2550,13 +2550,29 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* initscan() and we don't want to allocate memory again
*/
if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ scan->rs_scan.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
- scan->rs_key = NULL;
+ scan->rs_scan.rs_key = NULL;
initscan(scan, key, false);
- return scan;
+ return (StorageScanDesc)scan;
+}
+
+static ParallelHeapScanDesc
+heapam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
+
+ return scan->rs_parallel;
+}
+
+static HeapPageScanDesc
+heapam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
+
+ return &scan->rs_pagescan;
}
/* ----------------
@@ -2564,21 +2580,23 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* ----------------
*/
static void
-heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+heapam_rescan(StorageScanDesc sscan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
if (set_params)
{
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_scan.rs_snapshot);
}
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* reinitialize scan descriptor
@@ -2609,29 +2627,31 @@ heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
* ----------------
*/
static void
-heapam_endscan(HeapScanDesc scan)
+heapam_endscan(StorageScanDesc sscan)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* decrement relation reference count and free scan descriptor storage
*/
- RelationDecrementReferenceCount(scan->rs_rd);
+ RelationDecrementReferenceCount(scan->rs_scan.rs_rd);
- if (scan->rs_key)
- pfree(scan->rs_key);
+ if (scan->rs_scan.rs_key)
+ pfree(scan->rs_scan.rs_key);
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ UnregisterSnapshot(scan->rs_scan.rs_snapshot);
pfree(scan);
}
@@ -2643,12 +2663,14 @@ heapam_endscan(HeapScanDesc scan)
* ----------------
*/
static void
-heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+heapam_scan_update_snapshot(StorageScanDesc sscan, Snapshot snapshot)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(IsMVCCSnapshot(snapshot));
RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
+ scan->rs_scan.rs_snapshot = snapshot;
scan->rs_temp_snap = true;
}
@@ -2677,7 +2699,7 @@ heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
static StorageTuple
-heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+heapam_getnext(StorageScanDesc sscan, ScanDirection direction)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2685,11 +2707,11 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
HEAPAMDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2703,7 +2725,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
*/
HEAPAMDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return &(scan->rs_ctup);
}
@@ -2723,7 +2745,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
#endif
static TupleTableSlot *
-heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+heapam_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2731,11 +2753,11 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2750,11 +2772,32 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
*/
HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
slot, InvalidBuffer, true);
}
+static StorageTuple
+heapam_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+ Page dp;
+ ItemId lp;
+
+ dp = (Page) BufferGetPage(scan->rs_scan.rs_cbuf);
+ lp = PageGetItemId(dp, offset);
+ Assert(ItemIdIsNormal(lp));
+
+ scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+ scan->rs_ctup.t_len = ItemIdGetLength(lp);
+ scan->rs_ctup.t_tableOid = scan->rs_scan.rs_rd->rd_id;
+ ItemPointerSet(&scan->rs_ctup.t_self, blkno, offset);
+
+ pgstat_count_heap_fetch(scan->rs_scan.rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
/*
* heap_fetch - retrieve tuple with given tid
*
@@ -3069,18 +3112,18 @@ heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
static void
-heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+heapam_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+ Assert(!scan->rs_scan.rs_inited); /* else too late to change */
+ Assert(!scan->rs_pagescan.rs_syncscan); /* else rs_startblock is significant */
/* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+ Assert(startBlk == 0 || startBlk < scan->rs_pagescan.rs_nblocks);
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
+ scan->rs_pagescan.rs_startblock = startBlk;
+ scan->rs_pagescan.rs_numblocks = numBlks;
}
@@ -5086,12 +5129,26 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->scansetlimits = heapam_setscanlimits;
amroutine->scan_getnext = heapam_getnext;
amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_fetch_tuple_from_offset = heapam_fetch_tuple_from_offset;
amroutine->scan_end = heapam_endscan;
amroutine->scan_rescan = heapam_rescan;
amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
amroutine->tuple_freeze = heapam_freeze_tuple;
amroutine->hot_search_buffer = heapam_hot_search_buffer;
+ /*
+ * The following routine needs to be provided when the storage
+ * support parallel sequential scan
+ */
+ amroutine->scan_get_parallelheapscandesc = heapam_get_parallelheapscandesc;
+
+ /*
+ * The following routine needs to be provided when the storage
+ * support BitmapHeap and Sample Scans
+ */
+ amroutine->scan_get_heappagescandesc = heapam_get_heappagescandesc;
+
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 76b94dc..af59d46 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -54,7 +54,7 @@
* Caller must hold a suitable lock on the correct relation.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
{
Snapshot snapshot;
@@ -67,6 +67,25 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
true, true, true, false, false, true);
}
+ParallelHeapScanDesc
+storageam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_get_parallelheapscandesc(sscan);
+}
+
+HeapPageScanDesc
+storageam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ /*
+ * Planner should have already validated whether the current storage
+ * supports Page scans are not? This function will be called only from
+ * Bitmap Heap scan and sample scan
+ */
+ Assert (sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc != NULL);
+
+ return sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc(sscan);
+}
+
/*
* heap_setscanlimits - restrict range of a heapscan
*
@@ -74,7 +93,7 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
}
@@ -104,7 +123,7 @@ storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numB
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -112,7 +131,7 @@ storage_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -122,7 +141,7 @@ storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -132,7 +151,7 @@ storage_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -140,7 +159,7 @@ storage_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -155,7 +174,7 @@ storage_beginscan_sampling(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-storage_rescan(HeapScanDesc scan,
+storage_rescan(StorageScanDesc scan,
ScanKey key)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
@@ -171,7 +190,7 @@ storage_rescan(HeapScanDesc scan,
* ----------------
*/
void
-storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
@@ -186,7 +205,7 @@ storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
* ----------------
*/
void
-storage_endscan(HeapScanDesc scan)
+storage_endscan(StorageScanDesc scan)
{
scan->rs_rd->rd_stamroutine->scan_end(scan);
}
@@ -199,23 +218,29 @@ storage_endscan(HeapScanDesc scan)
* ----------------
*/
void
-storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot)
{
scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
}
StorageTuple
-storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+storage_getnext(StorageScanDesc sscan, ScanDirection direction)
{
return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
}
TupleTableSlot*
-storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
}
+StorageTuple
+storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_fetch_tuple_from_offset(sscan, blkno, offset);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index db5c93b..bcdef50 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -478,10 +478,10 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
}
else
{
- HeapScanDesc scan = sysscan->scan;
+ StorageScanDesc scan = sysscan->scan;
Assert(IsMVCCSnapshot(scan->rs_snapshot));
- Assert(tup == &scan->rs_ctup);
+ //hari Assert(tup == &scan->rs_ctup);
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index e270cbc..8793b95 100644
--- a/src/backend/access/tablesample/system.c
+++ b/src/backend/access/tablesample/system.c
@@ -183,7 +183,7 @@ static BlockNumber
system_nextsampleblock(SampleScanState *node)
{
SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc scan = node->pagescan;
BlockNumber nextblock = sampler->nextblock;
uint32 hashinput[2];
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 975cd5b..17c1e1f 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -564,7 +564,7 @@ boot_openrel(char *relname)
int i;
struct typmap **app;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
@@ -880,7 +880,7 @@ gettype(char *type)
{
int i;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
struct typmap **app;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d2a8a06..281e3a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
{
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 68c46f7..2564b14 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1897,7 +1897,7 @@ index_update_stats(Relation rel,
ReindexIsProcessingHeap(RelationRelationId))
{
/* don't assume syscache will work */
- HeapScanDesc pg_class_scan;
+ StorageScanDesc pg_class_scan;
ScanKeyData key[1];
ScanKeyInit(&key[0],
@@ -2210,7 +2210,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
{
bool is_system_catalog;
bool checking_uniqueness;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2649,7 +2649,7 @@ IndexCheckExclusion(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2963,7 +2963,7 @@ validate_index_heapscan(Relation heapRelation,
Snapshot snapshot,
v_i_state *state)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index b8137f2..1a766ac 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -945,7 +945,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
Snapshot snapshot;
TupleDesc tupdesc;
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 1d048e6..842abcc 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -151,7 +151,7 @@ RemoveConversionById(Oid conversionOid)
{
Relation rel;
HeapTuple tuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scanKeyData;
ScanKeyInit(&scanKeyData,
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 517e310..63324cf 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -171,7 +171,7 @@ void
DropSetting(Oid databaseid, Oid roleid)
{
Relation relsetting;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 145e3c1..5e3915b 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index a51f2e4..050dfa3 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -377,7 +377,7 @@ void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData skey[2];
HeapTuple tup;
int nkeys = 0;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e0f6973..ccdbe70 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -746,7 +746,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
Datum *values;
bool *isnull;
IndexScanDesc indexScan;
- HeapScanDesc heapScan;
+ StorageScanDesc heapScan;
bool use_wal;
bool is_system_catalog;
TransactionId OldestXmin;
@@ -1638,7 +1638,7 @@ static List *
get_tables_to_cluster(MemoryContext cluster_context)
{
Relation indRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData entry;
HeapTuple indexTuple;
Form_pg_index index;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 00e71e3..98c09d3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2022,7 +2022,7 @@ CopyTo(CopyState cstate)
{
Datum *values;
bool *nulls;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 39850b1..0913577 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -99,7 +99,7 @@ static int errdetail_busy_db(int notherbackends, int npreparedxacts);
Oid
createdb(ParseState *pstate, const CreatedbStmt *stmt)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
Relation rel;
Oid src_dboid;
Oid src_owner;
@@ -1872,7 +1872,7 @@ static void
remove_dbtablespaces(Oid db_id)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
@@ -1939,7 +1939,7 @@ check_db_file_conflict(Oid db_id)
{
bool result = false;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 46bc3da..f8cbfcc 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1875,7 +1875,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{
Oid objectOid;
Relation relationRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scan_keys[1];
HeapTuple tuple;
MemoryContext private_context;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6b6beb9..0327030 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4481,7 +4481,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
@@ -5053,7 +5053,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
@@ -8202,7 +8202,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Expr *origexpr;
ExprState *exprstate;
TupleDesc tupdesc;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
MemoryContext oldcxt;
@@ -8285,7 +8285,7 @@ validateForeignKeyConstraint(char *conname,
Oid pkindOid,
Oid constraintOid)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
@@ -10737,7 +10737,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ListCell *l;
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index cdfa8ff..be3c0db 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -402,7 +402,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
char *tablespacename = stmt->tablespacename;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
Relation rel;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -913,7 +913,7 @@ RenameTableSpace(const char *oldname, const char *newname)
Oid tspId;
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
Form_pg_tablespace newform;
@@ -988,7 +988,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
{
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tup;
Oid tablespaceoid;
Datum datum;
@@ -1382,7 +1382,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
{
Oid result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -1428,7 +1428,7 @@ get_tablespace_name(Oid spc_oid)
{
char *result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c07f508..09b5b9f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2310,7 +2310,7 @@ AlterDomainNotNull(List *names, bool notNull)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
@@ -2706,7 +2706,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index e24ac9f..20f3a6c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -443,7 +443,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
* pg_class
*/
Relation pgclass;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
@@ -1091,7 +1091,7 @@ vac_truncate_clog(TransactionId frozenXID,
{
TransactionId nextXID = ReadNewTransactionId();
Relation relation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid oldestxid_datoid;
Oid minmulti_datoid;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 7921025..208ff91 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -54,14 +54,14 @@
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
-static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
+static void bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres);
static inline void BitmapDoneInitializingSharedState(
ParallelBitmapHeapState *pstate);
static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node,
TBMIterateResult *tbmres);
static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
static inline void BitmapPrefetch(BitmapHeapScanState *node,
- HeapScanDesc scan);
+ StorageScanDesc scan);
static bool BitmapShouldInitializeSharedState(
ParallelBitmapHeapState *pstate);
@@ -76,7 +76,8 @@ static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
TIDBitmap *tbm;
TBMIterator *tbmiterator = NULL;
TBMSharedIterator *shared_tbmiterator = NULL;
@@ -92,6 +93,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scan = node->ss.ss_currentScanDesc;
+ pagescan = node->pagescan;
tbm = node->tbm;
if (pstate == NULL)
tbmiterator = node->tbmiterator;
@@ -191,8 +193,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
for (;;)
{
- Page dp;
- ItemId lp;
+ StorageTuple tuple;
CHECK_FOR_INTERRUPTS();
@@ -219,7 +220,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
- if (tbmres->blockno >= scan->rs_nblocks)
+ if (tbmres->blockno >= pagescan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
@@ -228,7 +229,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Fetch the current heap page and identify candidate tuples.
*/
- bitgetpage(scan, tbmres);
+ bitgetpage(node, tbmres);
if (tbmres->ntuples >= 0)
node->exact_pages++;
@@ -238,7 +239,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
- scan->rs_cindex = 0;
+ pagescan->rs_cindex = 0;
/* Adjust the prefetch target */
BitmapAdjustPrefetchTarget(node);
@@ -248,7 +249,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
- scan->rs_cindex++;
+ pagescan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -275,7 +276,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
- if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
+ if (pagescan->rs_cindex < 0 || pagescan->rs_cindex >= pagescan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
continue;
@@ -293,23 +294,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple
*/
- targoffset = scan->rs_vistuples[scan->rs_cindex];
- dp = (Page) BufferGetPage(scan->rs_cbuf);
- lp = PageGetItemId(dp, targoffset);
- Assert(ItemIdIsNormal(lp));
-
- scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- scan->rs_ctup.t_len = ItemIdGetLength(lp);
- scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
-
- pgstat_count_heap_fetch(scan->rs_rd);
+ targoffset = pagescan->rs_vistuples[pagescan->rs_cindex];
+ tuple = storage_fetch_tuple_from_offset(scan, tbmres->blockno, targoffset);
/*
* Set up the result slot to point to this tuple. Note that the slot
* acquires a pin on the buffer.
*/
- ExecStoreTuple(&scan->rs_ctup,
+ ExecStoreTuple(tuple,
slot,
scan->rs_cbuf,
false);
@@ -350,8 +342,10 @@ BitmapHeapNext(BitmapHeapScanState *node)
* interesting according to the bitmap, and visible according to the snapshot.
*/
static void
-bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
+bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres)
{
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
BlockNumber page = tbmres->blockno;
Buffer buffer;
Snapshot snapshot;
@@ -360,7 +354,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
- Assert(page < scan->rs_nblocks);
+ Assert(page < pagescan->rs_nblocks);
scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
scan->rs_rd,
@@ -403,7 +397,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
ItemPointerSet(&tid, page, offnum);
if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
- scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
+ pagescan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
@@ -419,23 +413,20 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
{
ItemId lp;
- HeapTupleData loctup;
+ StorageTuple loctup;
bool valid;
lp = PageGetItemId(dp, offnum);
if (!ItemIdIsNormal(lp))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- loctup.t_len = ItemIdGetLength(lp);
- loctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ loctup = storage_fetch_tuple_from_offset(scan, page, offnum);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, loctup, snapshot, buffer);
if (valid)
{
- scan->rs_vistuples[ntup++] = offnum;
- PredicateLockTuple(scan->rs_rd, &loctup, snapshot);
+ pagescan->rs_vistuples[ntup++] = offnum;
+ PredicateLockTuple(scan->rs_rd, loctup, snapshot);
}
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_rd, loctup,
buffer, snapshot);
}
}
@@ -443,7 +434,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ pagescan->rs_ntuples = ntup;
}
/*
@@ -569,7 +560,7 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
* BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
*/
static inline void
-BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
+BitmapPrefetch(BitmapHeapScanState *node, StorageScanDesc scan)
{
#ifdef USE_PREFETCH
ParallelBitmapHeapState *pstate = node->pstate;
@@ -724,7 +715,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -871,6 +862,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
0,
NULL);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
+
/*
* get the scan type from the relation descriptor.
*/
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 04f85e5..589505a 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -31,7 +31,7 @@ static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
static StorageTuple tablesample_getnext(SampleScanState *scanstate);
static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan); //hari
+ SampleScanState *scanstate);
/* ----------------------------------------------------------------
* Scan Support
@@ -66,8 +66,7 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
- InvalidBuffer,
+ node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -356,6 +355,7 @@ tablesample_init(SampleScanState *scanstate)
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
}
else
{
@@ -381,10 +381,11 @@ static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+ StorageTuple tuple;
Snapshot snapshot = scan->rs_snapshot;
- bool pagemode = scan->rs_pageatatime;
+ bool pagemode = pagescan->rs_pageatatime;
BlockNumber blockno;
Page page;
bool all_visible;
@@ -395,10 +396,9 @@ tablesample_getnext(SampleScanState *scanstate)
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
{
Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
return NULL;
}
if (tsm->NextSampleBlock)
@@ -406,13 +406,12 @@ tablesample_getnext(SampleScanState *scanstate)
blockno = tsm->NextSampleBlock(scanstate);
if (!BlockNumberIsValid(blockno))
{
- tuple->t_data = NULL;
return NULL;
}
}
else
- blockno = scan->rs_startblock;
- Assert(blockno < scan->rs_nblocks);
+ blockno = pagescan->rs_startblock;
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
scan->rs_inited = true;
}
@@ -455,14 +454,12 @@ tablesample_getnext(SampleScanState *scanstate)
if (!ItemIdIsNormal(itemid))
continue;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
- tuple->t_len = ItemIdGetLength(itemid);
- ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
+ tuple = storage_fetch_tuple_from_offset(scan, blockno, tupoffset);
if (all_visible)
visible = true;
else
- visible = SampleTupleVisible(tuple, tupoffset, scan);
+ visible = SampleTupleVisible(tuple, tupoffset, scanstate);
/* in pagemode, heapgetpage did this for us */
if (!pagemode)
@@ -493,14 +490,14 @@ tablesample_getnext(SampleScanState *scanstate)
if (tsm->NextSampleBlock)
{
blockno = tsm->NextSampleBlock(scanstate);
- Assert(!scan->rs_syncscan);
+ Assert(!pagescan->rs_syncscan);
finished = !BlockNumberIsValid(blockno);
}
else
{
/* Without NextSampleBlock, just do a plain forward seqscan. */
blockno++;
- if (blockno >= scan->rs_nblocks)
+ if (blockno >= pagescan->rs_nblocks)
blockno = 0;
/*
@@ -513,10 +510,10 @@ tablesample_getnext(SampleScanState *scanstate)
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
+ if (pagescan->rs_syncscan)
ss_report_location(scan->rs_rd, blockno);
- finished = (blockno == scan->rs_startblock);
+ finished = (blockno == pagescan->rs_startblock);
}
/*
@@ -528,12 +525,11 @@ tablesample_getnext(SampleScanState *scanstate)
ReleaseBuffer(scan->rs_cbuf);
scan->rs_cbuf = InvalidBuffer;
scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
scan->rs_inited = false;
return NULL;
}
- Assert(blockno < scan->rs_nblocks);
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
/* Re-establish state for new page */
@@ -548,16 +544,19 @@ tablesample_getnext(SampleScanState *scanstate)
/* Count successfully-fetched tuples as heap fetches */
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return tuple;
}
/*
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, SampleScanState *scanstate)
{
- if (scan->rs_pageatatime)
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+
+ if (pagescan->rs_pageatatime)
{
/*
* In pageatatime mode, heapgetpage() already did visibility checks,
@@ -569,12 +568,12 @@ SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan
* gain to justify the restriction.
*/
int start = 0,
- end = scan->rs_ntuples - 1;
+ end = pagescan->rs_ntuples - 1;
while (start <= end)
{
int mid = (start + end) / 2;
- OffsetNumber curoffset = scan->rs_vistuples[mid];
+ OffsetNumber curoffset = pagescan->rs_vistuples[mid];
if (tupoffset == curoffset)
return true;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 839d3a6..ec67024 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -316,9 +316,10 @@ void
ExecSeqScanReInitializeDSM(SeqScanState *node,
ParallelContext *pcxt)
{
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ ParallelHeapScanDesc pscan;
- heap_parallelscan_reinitialize(scan->rs_parallel);
+ pscan = storageam_get_parallelheapscandesc(node->ss.ss_currentScanDesc);
+ heap_parallelscan_reinitialize(pscan);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a1ebd4a..4503593 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -248,7 +248,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amsearchnulls = amroutine->amsearchnulls;
info->amcanparallel = amroutine->amcanparallel;
info->amhasgettuple = (amroutine->amgettuple != NULL);
- info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amhasgetbitmap = ((amroutine->amgetbitmap != NULL)
+ && (relation->rd_stamroutine->scan_get_heappagescandesc != NULL));
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index fec203d..9f505a5 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1864,7 +1864,7 @@ get_database_list(void)
{
List *dblist = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
@@ -1930,7 +1930,7 @@ do_autovacuum(void)
{
Relation classRel;
HeapTuple tuple;
- HeapScanDesc relScan;
+ StorageScanDesc relScan;
Form_pg_database dbForm;
List *table_oids = NIL;
List *orphan_oids = NIL;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 74113a7..86db35d 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1207,7 +1207,7 @@ pgstat_collect_oids(Oid catalogid)
HTAB *htab;
HASHCTL hash_ctl;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index fca56d4..0e840c5 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -107,7 +107,7 @@ get_subscription_list(void)
{
List *res = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4924dac..7122987 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -420,7 +420,7 @@ DefineQueryRewrite(char *rulename,
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
Snapshot snapshot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 82c042a..48b6924 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1204,7 +1204,7 @@ static bool
ThereIsAtLeastOneRole(void)
{
Relation pg_authid_rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
bool result;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4cddd73..42c2686 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -97,6 +97,8 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
#define heap_close(r,l) relation_close(r,l)
/* struct definitions appear in relscan.h */
+typedef struct HeapPageScanDescData *HeapPageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
@@ -106,7 +108,7 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
+extern void heapgetpage(StorageScanDesc sscan, BlockNumber page);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 147f862..8e00c84 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/htup_details.h"
#include "access/itup.h"
#include "access/tupdesc.h"
@@ -42,40 +43,54 @@ typedef struct ParallelHeapScanDescData
char phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelHeapScanDescData;
-typedef struct HeapScanDescData
+typedef struct StorageScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
Snapshot rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
ScanKey rs_key; /* array of scan key descriptors */
- bool rs_bitmapscan; /* true if this is really a bitmap scan */
- bool rs_samplescan; /* true if this is really a sample scan */
+
+ /* scan current state */
+ bool rs_inited; /* false = scan not init'd yet */
+ BlockNumber rs_cblock; /* current block # in scan, if any */
+ Buffer rs_cbuf; /* current buffer in scan, if any */
+} StorageScanDescData;
+
+typedef struct HeapPageScanDescData
+{
bool rs_pageatatime; /* verify visibility page-at-a-time? */
- bool rs_allow_strat; /* allow or disallow use of access strategy */
- bool rs_allow_sync; /* allow or disallow use of syncscan */
- bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* total number of blocks in rel */
BlockNumber rs_startblock; /* block # to start at */
BlockNumber rs_numblocks; /* max number of blocks to scan */
+
/* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */
BufferAccessStrategy rs_strategy; /* access strategy for reads */
bool rs_syncscan; /* report location to syncscan logic? */
- /* scan current state */
- bool rs_inited; /* false = scan not init'd yet */
- HeapTupleData rs_ctup; /* current tuple in scan, if any */
- BlockNumber rs_cblock; /* current block # in scan, if any */
- Buffer rs_cbuf; /* current buffer in scan, if any */
- /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- ParallelHeapScanDesc rs_parallel; /* parallel scan information */
-
/* these fields only used in page-at-a-time mode and for bitmap scans */
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
+} HeapPageScanDescData;
+
+typedef struct HeapScanDescData
+{
+ /* scan parameters */
+ StorageScanDescData rs_scan; /* */
+ HeapPageScanDescData rs_pagescan;
+ bool rs_bitmapscan; /* true if this is really a bitmap scan */
+ bool rs_samplescan; /* true if this is really a sample scan */
+ bool rs_allow_strat; /* allow or disallow use of access strategy */
+ bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
+
+ HeapTupleData rs_ctup; /* current tuple in scan, if any */
+
+ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+ ParallelHeapScanDesc rs_parallel; /* parallel scan information */
} HeapScanDescData;
/*
@@ -149,12 +164,12 @@ typedef struct ParallelIndexScanDescData
char ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelIndexScanDescData;
-/* Struct for heap-or-index scans of system tables */
+/* Struct for storage-or-index scans of system tables */
typedef struct SysScanDescData
{
Relation heap_rel; /* catalog being scanned */
Relation irel; /* NULL if doing heap scan */
- HeapScanDesc scan; /* only valid in heap-scan case */
+ StorageScanDesc scan; /* only valid in storage-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 507af71..f335c79 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,7 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
-typedef void *StorageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef union tuple_data
{
@@ -37,29 +37,31 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
-extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
-
-extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
-extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+extern ParallelHeapScanDesc storageam_get_parallelheapscandesc(StorageScanDesc sscan);
+extern HeapPageScanDesc storageam_get_heappagescandesc(StorageScanDesc sscan);
+extern void storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern StorageScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync);
-extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_endscan(HeapScanDesc scan);
-extern void storage_rescan(HeapScanDesc scan, ScanKey key);
-extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern void storage_endscan(StorageScanDesc scan);
+extern void storage_rescan(StorageScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
-extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
-extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_getnext(StorageScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset);
extern void storage_get_latest_tid(Relation relation,
Snapshot snapshot,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index c2e6dc2..d849b86 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -20,7 +20,7 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
-typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+typedef StorageScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -30,22 +30,26 @@ typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
bool is_bitmapscan,
bool is_samplescan,
bool temp_snap);
-typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+typedef ParallelHeapScanDesc (*scan_get_parallelheapscandesc_hook) (StorageScanDesc scan);
+typedef HeapPageScanDesc (*scan_get_heappagescandesc_hook) (StorageScanDesc scan);
+typedef void (*scan_setlimits_hook) (StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
/* must return a TupleTableSlot? */
-typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+typedef StorageTuple (*scan_getnext_hook) (StorageScanDesc scan,
ScanDirection direction);
-typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (StorageScanDesc scan,
ScanDirection direction, TupleTableSlot *slot);
+typedef StorageTuple (*scan_fetch_tuple_from_offset_hook) (StorageScanDesc scan,
+ BlockNumber blkno, OffsetNumber offset);
-typedef void (*scan_end_hook) (HeapScanDesc scan);
+typedef void (*scan_end_hook) (StorageScanDesc scan);
-typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
-typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+typedef void (*scan_getpage_hook) (StorageScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+typedef void (*scan_update_snapshot_hook) (StorageScanDesc scan, Snapshot snapshot);
typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
@@ -161,9 +165,12 @@ typedef struct StorageAmRoutine
/* Operations on relation scans */
scan_begin_hook scan_begin;
+ scan_get_parallelheapscandesc_hook scan_get_parallelheapscandesc;
+ scan_get_heappagescandesc_hook scan_get_heappagescandesc;
scan_setlimits_hook scansetlimits;
scan_getnext_hook scan_getnext;
scan_getnext_slot_hook scan_getnextslot;
+ scan_fetch_tuple_from_offset_hook scan_fetch_tuple_from_offset;
scan_end_hook scan_end;
scan_getpage_hook scan_getpage;
scan_rescan_hook scan_rescan;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1ed1864..1037f12 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tupconvert.h"
#include "executor/instrument.h"
#include "lib/pairingheap.h"
@@ -1097,7 +1098,7 @@ typedef struct ScanState
{
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
- HeapScanDesc ss_currentScanDesc;
+ StorageScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
@@ -1118,6 +1119,7 @@ typedef struct SeqScanState
typedef struct SampleScanState
{
ScanState ss;
+ HeapPageScanDesc pagescan;
List *args; /* expr states for TABLESAMPLE params */
ExprState *repeatable; /* expr state for REPEATABLE expr */
/* use struct pointer to avoid including tsmapi.h here */
@@ -1342,6 +1344,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
+ HeapPageScanDesc pagescan;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
--
2.7.4.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.6K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/4-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From 7c4bcfa472fd96c5c8a39309fc1394e94831e395 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/8] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 4 ++++
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa..33079c1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818..d4ac340 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5172,16 +5174,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be79353..0a7e0a3 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b..2c3e33c 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
@@ -83,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f73c6c6..6b675b0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3879,6 +3879,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452..ea352fa 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7..3bc9607 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1711,11 +1711,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 2945966..f1f58a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1153,15 +1153,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.7.4.windows.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.3K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/5-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From bef98460128bdaed155017b54f1b89cde2a16c2b Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/8] Storage AM API hooks and related functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 237 +++++++++++++++++++++++++++++++
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
8 files changed, 427 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496..02a3909 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000..88827e7
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000..def2029
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine *HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68..6459435 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000..95fe028
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,237 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*scan_end_hook) (HeapScanDesc scan);
+
+
+typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*tuple_insert_hook) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*tuple_delete_hook) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*tuple_update_hook) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*tuple_fetch_hook) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*tuple_lock_hook) (Relation relation,
+ ItemPointer tid,
+ StorageTuple *tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*multi_insert_hook) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*tuple_freeze_hook) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*tuple_get_latest_tid_hook) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data (*get_tuple_data_hook) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple (*tuple_from_datum_hook) (Datum data, Oid tableoid);
+
+typedef bool (*tuple_is_heaponly_hook) (StorageTuple tuple);
+
+typedef void (*slot_store_tuple_hook) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*slot_clear_tuple_hook) (TupleTableSlot *slot);
+typedef Datum (*slot_getattr_hook) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*slot_virtualize_tuple_hook) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*slot_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*slot_min_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*slot_update_tableoid_hook) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*speculative_finish_hook) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*speculative_abort_hook) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*relation_sync_hook) (Relation relation);
+
+typedef bool (*snapshot_satisfies_hook) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*snapshot_satisfies_update_hook) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*snapshot_satisfies_vacuum_hook) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ slot_store_tuple_hook slot_store_tuple;
+ slot_virtualize_tuple_hook slot_virtualize_tuple;
+ slot_clear_tuple_hook slot_clear_tuple;
+ slot_getattr_hook slot_getattr;
+ slot_tuple_hook slot_tuple;
+ slot_min_tuple_hook slot_min_tuple;
+ slot_update_tableoid_hook slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine* (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ scan_begin_hook scan_begin;
+ scan_setlimits_hook scansetlimits;
+ scan_getnext_hook scan_getnext;
+ scan_getnext_slot_hook scan_getnextslot;
+ scan_end_hook scan_end;
+ scan_getpage_hook scan_getpage;
+ scan_rescan_hook scan_rescan;
+ scan_update_snapshot_hook scan_update_snapshot;
+ hot_search_buffer_hook hot_search_buffer; /* heap_hot_search_buffer */
+
+ // heap_sync_function heap_sync; /* heap_sync */
+ /* not implemented */
+ // parallelscan_estimate_function parallelscan_estimate; /* heap_parallelscan_estimate */
+ // parallelscan_initialize_function parallelscan_initialize; /* heap_parallelscan_initialize */
+ // parallelscan_begin_function parallelscan_begin; /* heap_beginscan_parallel */
+
+ /* Operations on physical tuples */
+ tuple_insert_hook tuple_insert; /* heap_insert */
+ tuple_update_hook tuple_update; /* heap_update */
+ tuple_delete_hook tuple_delete; /* heap_delete */
+ tuple_fetch_hook tuple_fetch; /* heap_fetch */
+ tuple_lock_hook tuple_lock; /* heap_lock_tuple */
+ multi_insert_hook multi_insert; /* heap_multi_insert */
+ tuple_freeze_hook tuple_freeze; /* heap_freeze_tuple */
+ tuple_get_latest_tid_hook tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ get_tuple_data_hook get_tuple_data;
+ tuple_is_heaponly_hook tuple_is_heaponly;
+ tuple_from_datum_hook tuple_from_datum;
+
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no need
+ * for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ speculative_finish_hook speculative_finish;
+ speculative_abort_hook speculative_abort;
+
+
+ relation_sync_hook relation_sync; /* heap_sync */
+
+ snapshot_satisfies_hook snapshot_satisfies[END_OF_VISIBILITY];
+ snapshot_satisfies_update_hook snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ snapshot_satisfies_vacuum_hook snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine *GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine *GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine *GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6b675b0..1dfb711 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -558,6 +558,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3..108df9b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -496,6 +496,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 9a3b56e..de75e01 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
2.7.4.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/6-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From bb4ef35668ba13430fe669d902f554290f50fa77 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/8] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 117 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e3780..40cbc67 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1885,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2027,6 +2109,11 @@ formrdesc(const char *relationName, Oid relationReltype,
RelationInitPhysicalAddr(relation);
/*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
+ /*
* initialize the rel-has-index flag, using hardwired knowledge
*/
if (IsBootstrapProcessingMode())
@@ -2354,6 +2441,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3457,13 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3985,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert (relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5715,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..82d8a53 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -161,6 +161,12 @@ typedef struct RelationData
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
+ /*
* index access support info (used only for an index relation)
*
* Note: only default support procs for each opclass are cached, namely
@@ -428,6 +434,12 @@ typedef struct ViewOptions
#define RelationGetDescr(relation) ((relation)->rd_att)
/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
+/*
* RelationGetRelationName
* Returns the rel's name.
*
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cef..03d996f 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.7.4.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (145.7K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/7-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From cfd2a2b9181eb15c98cc424deca68024f35c6718 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:38:28 +1000
Subject: [PATCH 4/8] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 10 +-
contrib/pgrowlocks/pgrowlocks.c | 3 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 23 +-
src/backend/access/heap/heapam_common.c | 162 +++
src/backend/access/heap/heapam_storage.c | 1622 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 4 +-
src/backend/commands/analyze.c | 2 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 7 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1809 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/access/htup.h | 3 +-
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 66 +-
29 files changed, 1964 insertions(+), 1907 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575..10d47cd 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -51,7 +51,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +656,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +681,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +739,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65..5f076ef 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,7 +150,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
GetCurrentCommandId(false),
scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf0613..284eabc 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7a91cc3..f7b68a8 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909..e6bc18e 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f038..c21d6f8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -1950,7 +1953,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2097,7 +2100,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2271,7 +2274,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3095,7 +3098,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3206,7 +3209,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3666,7 +3669,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3847,7 +3850,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4598,7 +4601,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000..502f6db
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
+
+
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 88827e7..1bd4bfa 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,6 +48,1617 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
+ */
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
+ Buffer buffer)
+{
+ return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+ != HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except for known-committed
+ * XIDs which could not be ours anyway.
+ */
+static bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
@@ -54,5 +1666,15 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
+ amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
+ amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
+ amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
+ amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
+ amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] = HeapTupleSatisfiesHistoricMVCC;
+ amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] = HeapTupleSatisfiesNonVacuumable;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac..7425068 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index bd560e4..191f088 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da0..01321a2 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f03..0240df7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,7 +2359,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 08fc18e..2611cca 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1069,7 +1069,7 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
OldestXmin,
targbuffer))
{
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e..dbcc5bc 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 45b1859..dccda5a 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -975,7 +975,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2140,7 +2140,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index f7e55e0..60a6cb0 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -428,7 +428,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 49586a3..d1afda4 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1307,7 +1308,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a83..6a118d1 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index fba57a0..095d1ed 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 251a359..4fbad9f 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6..5a6d216 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa..f17b1c5 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 294ab70..f823da0 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2040,7 +2040,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2137,7 +2137,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index bbac408..0000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1809 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-/* local functions */
-static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleSatisfiesNonVacuumable
- *
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
- *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
- */
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
- != HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except for known-committed
- * XIDs which could not be ours anyway.
- */
-static bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000..ff63cf3
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6459435..5b0068a 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -29,11 +29,12 @@ typedef MinimalTupleData *MinimalTuple;
typedef enum tuple_visibility_type
{
MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
- SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
END_OF_VISIBILITY
} tuple_visibility_type;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc..b8b823b 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf51977..5752ee4 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -52,7 +52,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index de75e01..67a8b1e 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,48 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
-
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies[(snapshot)->visibility_type]) (tuple, snapshot, buffer))
/*
* We don't provide a static SnapshotDirty variable because it would be
@@ -91,14 +52,14 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -106,8 +67,19 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
2.7.4.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (60.2K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/8-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 881ab5f4043027496374d0d6f17bf400250dd3f1 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 17:24:35 +1000
Subject: [PATCH 5/8] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 1 +
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 15 +-
src/backend/executor/execExprInterp.c | 26 +-
src/backend/executor/execReplication.c | 92 ++-----
src/backend/executor/execTuples.c | 269 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 644 insertions(+), 575 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528..5ed0f15 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1022,111 +1023,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
-/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
* It is functionally equivalent to heap_getattr, but fetches of
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 502f6db..6cb6c5b 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -159,4 +159,408 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *)palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple)tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple)tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine*
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 1bd4bfa..7d7ac75 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1665,6 +1665,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->slot_storageam = heapam_storage_slot_handler;
amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cfa3f05..8456bfd 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2695,7 +2695,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210c..a0ec444 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376..b440740 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 269c9e1..f891cd1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2348,7 +2348,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2429,7 +2429,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2777,7 +2777,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2819,7 +2819,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2928,7 +2928,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3960,14 +3960,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index bd8a15d..4775738 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -503,12 +503,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+ //hari Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(innerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -519,12 +519,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
-
+ Assert(outerslot->tts_storage != NULL);
+ //hari Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
+
+ *op->resvalue = slot_getattr(outerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -535,12 +535,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+ //hari Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(scanslot,
+ attnum,
op->resnull);
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 5a75e02..6700f0a 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,7 +226,8 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ TupleTableSlot *scanslot;
+ HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
TransactionId xwait;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d..b7a2cbc 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -206,6 +206,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
}
/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert (slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
+/* --------------------------------
* ExecDropSingleTupleTableSlot
*
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,18 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
-
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +459,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +540,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +549,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +568,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +598,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +645,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +653,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +664,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +697,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +717,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,19 +728,11 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
/*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
* mustn't leave any dangling pass-by-reference datums in tts_values.
@@ -768,17 +741,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6..02f6c81 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d1afda4..3b23df7 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -403,7 +403,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -416,7 +416,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -434,7 +434,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -743,7 +743,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -894,7 +894,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -955,7 +955,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -974,7 +974,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -988,7 +988,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1004,7 +1004,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1120,7 +1120,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 6afcd1a..81964d7 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -305,7 +305,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
}
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index bc6d824..a60be00 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -724,9 +724,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3..1fe15ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index fa04a63..9539d67 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -21,6 +21,19 @@
#include "storage/bufpage.h"
/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
+/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
* a tuple, plus the size of the null-values bitmap (at 1 bit per column),
@@ -653,7 +666,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce..84dc293 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.7.4.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (246.1K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/9-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From d445b3b28a5b2102110d1344abba57954568a326 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Thu, 7 Sep 2017 14:48:24 +1000
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM.
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2737 ++++--------------------------
src/backend/access/heap/heapam_storage.c | 2153 ++++++++++++++++++++++-
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 306 ++++
src/backend/access/heap/tuptoaster.c | 8 +-
src/backend/commands/copy.c | 29 +-
src/backend/commands/createas.c | 18 +-
src/backend/commands/matview.c | 10 +-
src/backend/commands/tablecmds.c | 5 +-
src/backend/commands/trigger.c | 43 +-
src/backend/executor/execMain.c | 120 +-
src/backend/executor/execReplication.c | 35 +-
src/backend/executor/nodeLockRows.c | 39 +-
src/backend/executor/nodeModifyTable.c | 177 +-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/htup_details.h | 1 +
src/include/access/storageam.h | 81 +
src/include/access/storageamapi.h | 21 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 6 +-
src/include/nodes/execnodes.h | 4 +-
26 files changed, 3290 insertions(+), 2717 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 5ed0f15..714c4d8 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -686,6 +686,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
}
/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
+/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
* which are of the length indicated by tupleDescriptor->natts
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e..162736f 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index c21d6f8..d20f211 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1838,158 +1745,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
}
/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
-/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
* On entry, *tid is the TID of a tuple (either a simple tuple, or the root
@@ -2172,130 +1927,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -2313,7 +1944,7 @@ heap_get_latest_tid(Relation relation,
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2596,7 +2227,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2664,412 +2295,110 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
-
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ Assert(ItemPointerIsValid(tid));
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
*/
if (PageIsAllVisible(page))
visibilitymap_pin(relation, block, &vmbuffer);
@@ -4504,7 +3833,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4522,724 +3851,34 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
-
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
+ if (*have_tuple_lock)
+ return true;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode))
@@ -5272,7 +3911,7 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
* window, but it's still possible to end up creating an unnecessary
* MultiXactId. Fortunately this is harmless.
*/
-static void
+void
compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
uint16 old_infomask2, TransactionId add_to_xmax,
LockTupleMode mode, bool is_update,
@@ -5319,916 +3958,226 @@ l5:
break;
case LockTupleNoKeyExclusive:
new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
- }
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
-}
-
-/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
- *
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
- *
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
- *
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
- */
-void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
-{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
-
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
-
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
+}
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
-}
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
@@ -6993,7 +4942,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7160,7 +5109,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7182,7 +5131,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7744,7 +5693,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9121,46 +7070,6 @@ heap2_redo(XLogReaderState *record)
}
/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
-/*
* Mask a heap page before performing consistency checks on it.
*/
void
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 7d7ac75..a0e3272 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -50,7 +50,13 @@
/* local functions */
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
/*-------------------------------------------------------------------------
*
* POSTGRES "time qualification" code, ie, tuple visibility rules.
@@ -1660,10 +1666,2149 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
-Datum
-heapam_storage_handler(PG_FUNCTION_ARGS)
+
+/*
+ * heap_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
{
- StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple->t_self));
+ tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple->t_len = ItemIdGetLength(lp);
+ tuple->t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is completely
+ * bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ /*Assert(slot->tts_speculativeToken != 0); This needs some update in toast */
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTupleData mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ priorXmax))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup.t_data->t_infomask;
+ old_infomask2 = mytup.t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup.t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup.t_data->t_infomask |= new_infomask;
+ mytup.t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data)HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data)HeapTupleHeaderGetUpdateXid(((HeapTuple)tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data)HeapTupleHeaderGetCmin(((HeapTuple)tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data)((HeapTuple)tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data)((HeapTuple)tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static bool
+heapam_tuple_is_heaopnly(StorageTuple tuple)
+{
+ return HeapTupleIsHeapOnly((HeapTuple)tuple);
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_finish = heapam_finish_speculative;
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_is_heaponly = heapam_tuple_is_heaopnly;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
amroutine->slot_storageam = heapam_storage_slot_handler;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 191f088..8fba61c 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000..d1d7364
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,306 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid,
+ options, bistate);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, cid,
+ crosscheck, wait, hufd, lockmode);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+
+/*
+ * storage_finish_speculative - mark speculative insertion as successful
+ *
+ * To successfully finish a speculative insertion we have to clear speculative
+ * token from tuple. To do so the t_ctid field, which will contain a
+ * speculative token value, is modified in place to point to the tuple itself,
+ * which is characteristic of a newly inserted ordinary tuple.
+ *
+ * NB: It is not ok to commit without either finishing or aborting a
+ * speculative insertion. We could treat speculative tuples of committed
+ * transactions implicitly as completed, but then we would have to be prepared
+ * to deal with speculative tokens on committed tuples. That wouldn't be
+ * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
+ * but clearing the token at completion isn't very expensive either.
+ * An explicit confirmation WAL record also makes logical decoding simpler.
+ */
+void
+storage_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_finish(relation, slot);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+bool
+storage_tuple_is_heaponly(Relation relation, StorageTuple tuple)
+{
+ return relation->rd_stamroutine->tuple_is_heaponly(tuple);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1da..d766a6e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,12 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8456bfd..c81ddf5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2694,8 +2695,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2758,19 +2757,18 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
+ &(slot->tts_tid),
estate,
false,
NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2866,7 +2864,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2899,12 +2897,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2923,7 +2921,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2940,8 +2938,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index a0ec444..d119149 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,24 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ slot->tts_tupleOid = InvalidOid;
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +619,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740..6102481 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,16 +492,15 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
- heap_insert(myState->transientrel,
- tuple,
+ storage_insert(myState->transientrel,
+ slot,
myState->output_cid,
myState->hi_options,
myState->bistate);
@@ -522,7 +522,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96354bd..ec6523e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4652,7 +4653,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate);
ResetExprContext(econtext);
@@ -4676,7 +4677,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index f891cd1..5d83506 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2411,17 +2411,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3061,9 +3065,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3079,11 +3084,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, &tuple,
estate->es_output_cid,
lockmode, LockWaitBlock,
false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3115,7 +3120,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3161,6 +3167,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3179,17 +3186,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3898,8 +3905,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -3974,10 +3981,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -3991,10 +3997,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4b594d4..a7127e4 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1894,7 +1894,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1974,7 +1974,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2021,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2480,7 +2480,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2497,7 +2498,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2528,7 +2531,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2561,14 +2564,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2576,12 +2579,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2595,8 +2598,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2617,7 +2620,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2646,17 +2650,20 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, tuple,
estate->es_output_cid,
lockmode, wait_policy,
false, &buffer, &hufd);
@@ -2695,12 +2702,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2722,10 +2732,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2734,7 +2740,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2743,8 +2749,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2762,7 +2768,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2770,17 +2778,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2826,7 +2836,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2845,7 +2855,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2873,7 +2883,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2904,8 +2914,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2929,11 +2937,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
erm,
datum,
&updated);
- if (copyTuple == NULL)
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2947,23 +2955,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2973,19 +2976,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3151,8 +3147,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 6700f0a..8d625b6 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,7 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -349,6 +351,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ tuple_data t_data;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
@@ -359,14 +362,15 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
+ t_data = storage_tuple_get_data(rel, tuple, TID);
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(t_data.tid),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +394,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -426,8 +430,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* OK, update the tuple and index entries for it */
simple_heap_update(rel, tid, tuple);
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
+ if (resultRelInfo->ri_NumIndices > 0 && !storage_tuple_is_heaponly(rel, tuple))
recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 9389560..f06f34a 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple)(&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
estate->es_output_cid,
lockmode, erm->waitPolicy, true,
&buffer, &hufd);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 3b23df7..da4ec94 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
+ Buffer buffer;
Relation rel = relinfo->ri_RelationDesc;
- Buffer buffer;
- HeapTupleData tuple;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate,
TupleTableSlot *result = NULL;
/*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
- /*
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -371,19 +371,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where the
+ * plan tree can return a tuple extracted literally from some table with
+ * the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -401,9 +413,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -415,9 +424,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -433,14 +439,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -460,7 +464,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -551,24 +556,24 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
HEAP_INSERT_SPECULATIVE,
NULL);
/* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, true, &specConflict,
arbiterIndexes);
/* adjust the tuple's state accordingly */
if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
+ storage_finish_speculative(resultRelationDesc, slot);
else
- heap_abort_speculative(resultRelationDesc, tuple);
+ storage_abort_speculative(resultRelationDesc, slot);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -596,17 +601,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL,
arbiterIndexes);
}
@@ -616,11 +618,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -671,7 +673,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -716,8 +718,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -743,8 +743,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -758,7 +760,7 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
+ result = storage_delete(resultRelationDesc, tupleid,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -858,7 +860,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -872,20 +874,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -894,7 +895,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -931,14 +932,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1003,14 +1004,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1020,7 +1021,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1056,7 +1057,7 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
+ result = storage_update(resultRelationDesc, tupleid, slot,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -1148,8 +1149,8 @@ lreplace:;
*
* If it's a HOT update, we mustn't insert new index entries.
*/
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && !storage_tuple_is_heaponly(resultRelationDesc, tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL, NIL);
}
@@ -1206,11 +1207,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1221,10 +1223,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1249,7 +1249,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1280,7 +1281,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1308,10 +1311,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1326,7 +1329,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1366,12 +1371,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1552,7 +1559,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7..8a6b217 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -332,12 +333,6 @@ TidNext(TidScanState *node)
numTids = node->tss_NumTids;
/*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
- /*
* Initialize or advance scan position, depending on direction.
*/
bBackward = ScanDirectionIsBackward(direction);
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ //hari
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 083f7d6..52779b7 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -354,7 +355,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -389,7 +390,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024..cdd45ef 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -133,40 +133,19 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -179,7 +158,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 1fe15ed..799b4ed 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 9539d67..16dfb3e 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -811,6 +811,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000..9502c92
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+extern void storage_finish_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 95fe028..c2e6dc2 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -13,32 +13,13 @@
#include "access/htup.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index aeb363f..0da9ac9 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -189,7 +189,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7708818..8704b7b 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,7 +196,7 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +204,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 90a60ab..1ed1864 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -2021,7 +2021,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
2.7.4.windows.1
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (178.0K, ../../CAJrrPGcYWE1g_jhTyOLDX+A-UUf8CADGwWBO8FGKqfQgRq9BCg@mail.gmail.com/10-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From efaf90b264d97f08233b6532717e1f5166f1f164 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:45:04 +1000
Subject: [PATCH 7/8] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1504 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1299 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 2 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 7 +-
src/backend/access/index/indexam.c | 3 +-
src/backend/access/nbtree/nbtinsert.c | 5 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 27 +-
src/backend/catalog/partition.c | 6 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 13 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 16 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 11 +-
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 12 +-
src/backend/executor/nodeIndexonlyscan.c | 4 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 21 +-
src/backend/executor/nodeSeqscan.c | 39 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 16 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 30 +-
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 42 +-
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 10 +-
src/include/executor/tqueue.h | 2 +-
src/include/funcapi.h | 2 +-
56 files changed, 1924 insertions(+), 1734 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 5f076ef..063e079 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index f7b68a8..eb33b26 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f211..b64fec8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,603 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
@@ -1186,317 +443,96 @@ relation_close(Relation relation, LOCKMODE lockmode)
UnlockRelationId(&relid, lockmode);
}
-
-/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
-}
-
+
/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
/* ----------------
@@ -1550,384 +586,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return &(scan->rs_ctup);
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- *
- * Note: if you change the criterion here for what is "dead", fix the
- * planner's get_actual_variable_range() function to match.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
-
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
*
@@ -4762,32 +3420,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index a0e3272..12a8f56 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1666,6 +1666,1094 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
/*
* heap_fetch - retrieve tuple with given tid
@@ -1818,9 +2906,210 @@ heap_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
/* ----------------------------------------------------------------
* storage AM support routines for heapam
@@ -3793,6 +5082,16 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 8fba61c..a475a85 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,7 +409,7 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
state->rs_cutoff_multi);
/*
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index d1d7364..76b94dc 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot*
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2..db5c93b 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,7 +395,7 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
nkeys, key,
true, false);
sysscan->iscan = NULL;
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255..349a127 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,7 +606,7 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
scan->xs_cbuf,
scan->xs_snapshot,
&scan->xs_ctup,
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fc..0e25e9a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,7 +326,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
&all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4..975cd5b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
@@ -573,18 +574,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -592,7 +593,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -903,25 +904,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a..d2a8a06 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0240df7..68c46f7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,7 +2280,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -2288,7 +2289,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
true); /* syncscan OK */
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,7 +3005,7 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -3014,7 +3015,7 @@ validate_index_heapscan(Relation heapRelation,
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 73eff17..b8137f2 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -986,7 +986,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -995,7 +995,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1011,7 +1011,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc3..1d048e6 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471b..517e310 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8..145e3c1 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71..a51f2e4 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc..e0f6973 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e5..6727d15 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c81ddf5..00e71e3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2028,10 +2028,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2043,7 +2043,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539..39850b1 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b61aaac..46bc3da 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1948,8 +1949,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1989,7 +1990,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ec6523e..6b6beb9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4540,7 +4540,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4548,7 +4548,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4661,7 +4661,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5064,9 +5064,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5078,7 +5078,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8243,7 +8243,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8251,7 +8251,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8266,7 +8266,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8321,9 +8321,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8352,7 +8352,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10802,8 +10802,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10862,7 +10862,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b..cdfa8ff 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 5d83506..a9452d4 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16ae..c07f508 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2315,8 +2316,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2345,7 +2346,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2711,8 +2712,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2755,7 +2756,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index faa1812..e24ac9f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -447,9 +448,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -469,7 +470,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
}
@@ -1121,9 +1122,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1160,7 +1161,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5..6ade9df 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189f..5e9daea 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 8d625b6..6f6861f 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,20 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +313,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b7a2cbc..0a5098d 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -681,7 +681,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -765,7 +765,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1085,7 +1085,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1093,7 +1093,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 42a4ca9..79b74ee 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0ae5873..d94169c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3097,7 +3097,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3212,7 +3212,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 60a6cb0..7921025 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
@@ -400,7 +401,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
@@ -685,7 +686,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -764,7 +765,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -865,7 +866,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
estate->es_snapshot,
0,
NULL);
@@ -1023,5 +1024,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 02f6c81..abec3a9 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 022d75b..5f4a294 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -252,7 +252,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -295,7 +295,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -303,7 +303,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index d20d466..ef9d9f1 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,7 +44,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -53,7 +53,7 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
@@ -399,7 +399,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -617,7 +617,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -689,12 +689,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5351cb8..f770bc4 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,7 +186,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
+ * provided in either StorageTuple or IndexTuple format. Conceivably an
* index AM might fill both fields, in which case we prefer the heap
* format, since it's probably a bit cheaper to fill a slot from.
*/
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 638b17b..7330ff9 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1..04f85e5 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+ HeapScanDesc scan); //hari
/* ----------------------------------------------------------------
* Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -244,7 +245,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,7 +350,7 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
scanstate->ss.ps.state->es_snapshot,
0, NULL,
scanstate->use_bulkread,
@@ -358,7 +359,7 @@ tablesample_init(SampleScanState *scanstate)
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
@@ -376,7 +377,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +555,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index d4ac939..839d3a6 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,7 +69,7 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
estate->es_snapshot,
0, NULL);
node->ss.ss_currentScanDesc = scandesc;
@@ -78,26 +78,7 @@ SeqNext(SeqScanState *node)
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,12 +251,12 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
+ storage_rescan(scan, /* scan desc */
NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
@@ -322,7 +303,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -353,5 +334,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 80be460..d55a752 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2091,7 +2091,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2159,7 +2159,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index afe231f..418c2a6 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -854,7 +854,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -962,7 +962,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1683,7 +1683,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1714,8 +1714,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 81964d7..81d0adc 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -192,12 +192,12 @@ static void TQSendRecordInfo(TQueueDestReceiver *tqueue, int32 typmod,
TupleDesc tupledesc);
static void TupleQueueHandleControlMessage(TupleQueueReader *reader,
Size nbytes, char *data);
-static HeapTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
+static StorageTuple TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes, HeapTupleHeader data);
-static HeapTuple TQRemapTuple(TupleQueueReader *reader,
+static StorageTuple TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple);
+ StorageTuple tuple);
static Datum TQRemap(TupleQueueReader *reader, TupleRemapInfo *remapinfo,
Datum value, bool *changed);
static Datum TQRemapArray(TupleQueueReader *reader, ArrayRemapInfo *remapinfo,
@@ -682,7 +682,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
shm_mq_result result;
@@ -737,7 +737,7 @@ TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
/*
* Handle a data message - that is, a tuple - from the remote side.
*/
-static HeapTuple
+static StorageTuple
TupleQueueHandleDataMessage(TupleQueueReader *reader,
Size nbytes,
HeapTupleHeader data)
@@ -766,11 +766,11 @@ TupleQueueHandleDataMessage(TupleQueueReader *reader,
/*
* Copy the given tuple, remapping any transient typmods contained in it.
*/
-static HeapTuple
+static StorageTuple
TQRemapTuple(TupleQueueReader *reader,
TupleDesc tupledesc,
TupleRemapInfo **field_remapinfo,
- HeapTuple tuple)
+ StorageTuple tuple)
{
Datum *values;
bool *isnull;
@@ -1128,7 +1128,7 @@ TupleQueueHandleControlMessage(TupleQueueReader *reader, Size nbytes,
static TupleRemapInfo *
BuildTupleRemapInfo(Oid typid, MemoryContext mycontext)
{
- HeapTuple tup;
+ StorageTuple tup;
Form_pg_type typ;
/* This is recursive, so it could be driven to stack overflow. */
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..fec203d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1882,9 +1882,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1911,7 +1911,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2042,13 +2042,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2134,7 +2134,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2142,8 +2142,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2189,7 +2189,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302..74113a7 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 6c89442..fca56d4 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 071b3a9..4924dac 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index eb6960d..82c042a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1208,10 +1209,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index cdd45ef..4cddd73 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,40 +106,16 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
+
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
@@ -147,8 +123,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -158,8 +132,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 799b4ed..66a96d7 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 9502c92..507af71 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,6 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -36,6 +37,34 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -44,6 +73,15 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
@@ -72,10 +110,6 @@ extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
-extern void storage_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid);
-
extern void storage_sync(Relation rel);
#endif
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d894..7f9bef1 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e..d466c99 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index a717ac6..4156767 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -27,7 +27,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle,
TupleDesc tupledesc);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2a..ab0e091 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.7.4.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-14 19:10 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-09-14 19:10 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Sep 14, 2017 at 8:17 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
> Instead of modifying the Bitmap Heap and Sample scan's to avoid referring
> the internal members of the HeapScanDesc, I divided the HeapScanDesc
> into two parts.
>
> 1. StorageScanDesc
> 2. HeapPageScanDesc
>
> The StorageScanDesc contains the minimal information that is required
> outside
> the Storage routine and this must be provided by all storage routines. This
> structure contains minimal information such as relation, snapshot, buffer
> and
> etc.
>
> The HeapPageScanDesc contains other extra information that is required for
> Bitmap Heap and Sample scans to work. This structure contains the
> information
> of blocks, visible offsets and etc. Currently this structure is used only
> in
> Bitmap Heap and Sample scan and it's supported contrib modules, except
> the pgstattuple module. The pgstattuple needs some additional changes.
>
> By adding additional storage API to return HeapPageScanDesc as it required
> by the Bitmap Heap and Sample scan's and this API is called only in these
> two scan's. And also these scan methods are choosen by the planner only
> when the storage routine supports to returning of HeapPageScanDesc API.
> Currently Implemented the planner support only for Bitmap, yet to do it
> for Sample scan.
>
> With the above approach, I removed all the references of HeapScanDesc
> outside the heap. The changes of this approach is available in the
> 0008-Remove-HeapScanDesc-usage-outside-heap.patch
>
> Suggestions/comments with the above approach.
>
For me, that's an interesting idea. Naturally, the way BitmapHeapScan and
SampleScan work even on very high-level is applicable only for some storage
AMs (i.e. heap-like storage AMs). For example, index-organized table
wouldn't ever support BitmapHeapScan, because it refers tuples by PK values
not TIDs. However, in this case, storage AM might have some alternative to
our BitmapHeapScan. So, index-organized table might have some compressed
representation of ordered PK values set and use it for bulk fetch of PK
index.
Therefore, I think it would be nice to make BitmapHeapScan an
heap-storage-AM-specific scan method while other storage AMs could provide
other storage-AM-specific scan methods. Probably it would be too much for
this patchset and should be done during one of next work cycles on storage
AM (I'm sure that such huge project as pluggable storage AMs would have
multiple iterations).
Similarly, SampleScans contain storage-AM-specific logic. For instance,
our SYSTEM sampling method fetches random blocks from heap providing high
performance way to sample heap. Coming back to the example of
index-organized table, it could provide it's own storage-AM-specific table
sampling methods including sophisticated PK tree traversal with fetching
random small ranges of PK. Given that tablesample methods are already
pluggable, making them storage-AM-specific would lead to user-visible
changes. I.e. tablesample method should be created for particular storage
AM or set of storage AMs. However, I didn't yet figure out what should API
exactly look like...
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-09-19 07:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-09-19 07:34 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Sep 15, 2017 at 5:10 AM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:
> On Thu, Sep 14, 2017 at 8:17 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
>> Instead of modifying the Bitmap Heap and Sample scan's to avoid referring
>> the internal members of the HeapScanDesc, I divided the HeapScanDesc
>> into two parts.
>>
>> 1. StorageScanDesc
>> 2. HeapPageScanDesc
>>
>> The StorageScanDesc contains the minimal information that is required
>> outside
>> the Storage routine and this must be provided by all storage routines.
>> This
>> structure contains minimal information such as relation, snapshot, buffer
>> and
>> etc.
>>
>> The HeapPageScanDesc contains other extra information that is required for
>> Bitmap Heap and Sample scans to work. This structure contains the
>> information
>> of blocks, visible offsets and etc. Currently this structure is used only
>> in
>> Bitmap Heap and Sample scan and it's supported contrib modules, except
>> the pgstattuple module. The pgstattuple needs some additional changes.
>>
>> By adding additional storage API to return HeapPageScanDesc as it required
>> by the Bitmap Heap and Sample scan's and this API is called only in these
>> two scan's. And also these scan methods are choosen by the planner only
>> when the storage routine supports to returning of HeapPageScanDesc API.
>> Currently Implemented the planner support only for Bitmap, yet to do it
>> for Sample scan.
>>
>> With the above approach, I removed all the references of HeapScanDesc
>> outside the heap. The changes of this approach is available in the
>> 0008-Remove-HeapScanDesc-usage-outside-heap.patch
>>
>> Suggestions/comments with the above approach.
>>
>
> For me, that's an interesting idea. Naturally, the way BitmapHeapScan and
> SampleScan work even on very high-level is applicable only for some storage
> AMs (i.e. heap-like storage AMs). For example, index-organized table
> wouldn't ever support BitmapHeapScan, because it refers tuples by PK values
> not TIDs. However, in this case, storage AM might have some alternative to
> our BitmapHeapScan. So, index-organized table might have some compressed
> representation of ordered PK values set and use it for bulk fetch of PK
> index.
>
> Therefore, I think it would be nice to make BitmapHeapScan an
> heap-storage-AM-specific scan method while other storage AMs could provide
> other storage-AM-specific scan methods. Probably it would be too much for
> this patchset and should be done during one of next work cycles on storage
> AM (I'm sure that such huge project as pluggable storage AMs would have
> multiple iterations).
>
Thanks for your opinion. Yes, that was my first thought of making these
two scan methods as part of the storage AMs. I feel the approach of just
exposing some additional hooks doesn't look good. This may need some
better infrastructure to provide storage AMs of their own scan methods.
Because of this reason, currently I developed the temporary approach of
separating HeapScanDesc into two structures.
> Similarly, SampleScans contain storage-AM-specific logic. For instance,
> our SYSTEM sampling method fetches random blocks from heap providing high
> performance way to sample heap. Coming back to the example of
> index-organized table, it could provide it's own storage-AM-specific table
> sampling methods including sophisticated PK tree traversal with fetching
> random small ranges of PK. Given that tablesample methods are already
> pluggable, making them storage-AM-specific would lead to user-visible
> changes. I.e. tablesample method should be created for particular storage
> AM or set of storage AMs. However, I didn't yet figure out what should API
> exactly look like...
>
Regarding SampleScans, I feel we can follow the same approach of supporting
particular sample methods with particular storage AMs similar like Bitmap
scans.
I didn't check it completely.
Rebased patches are attached.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0008-Remove-HeapScanDesc-usage-outside-heap.patch (90.7K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/3-0008-Remove-HeapScanDesc-usage-outside-heap.patch)
download | inline diff:
From 0f46bfe62208596bc841e64d56fdb01f0d9e5184 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Thu, 14 Sep 2017 12:34:30 +1000
Subject: [PATCH 8/8] Remove HeapScanDesc usage outside heap
HeapScanDesc is divided into two scan descriptors.
StorageScanDesc and HeapPageScanDesc.
StorageScanDesc has common members that are should
be available across all the storage routines and
HeapPageScanDesc is avaiable only for the storage
routine that supports Heap storage with page format.
The HeapPageScanDesc is used internally by the heapam
storage routine and also this is exposed to Bitmap Heap
and Sample scan's as they depend on the Heap page format.
while generating the Bitmap Heap and Sample scan's,
the planner now checks whether the storage routine
supports returning HeapPageScanDesc or not? Based on
this decision, the planner plans above two plans.
---
contrib/pgrowlocks/pgrowlocks.c | 4 +-
contrib/pgstattuple/pgstattuple.c | 10 +-
contrib/tsm_system_rows/tsm_system_rows.c | 18 +-
contrib/tsm_system_time/tsm_system_time.c | 8 +-
src/backend/access/heap/heapam.c | 37 +--
src/backend/access/heap/heapam_storage.c | 417 ++++++++++++++++-------------
src/backend/access/heap/storageam.c | 51 +++-
src/backend/access/index/genam.c | 4 +-
src/backend/access/tablesample/system.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 4 +-
src/backend/catalog/index.c | 8 +-
src/backend/catalog/partition.c | 2 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/cluster.c | 4 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 10 +-
src/backend/commands/tablespace.c | 10 +-
src/backend/commands/typecmds.c | 4 +-
src/backend/commands/vacuum.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 67 +++--
src/backend/executor/nodeSamplescan.c | 51 ++--
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/optimizer/util/plancat.c | 3 +-
src/backend/postmaster/autovacuum.c | 4 +-
src/backend/postmaster/pgstat.c | 2 +-
src/backend/replication/logical/launcher.c | 2 +-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/utils/init/postinit.c | 2 +-
src/include/access/heapam.h | 4 +-
src/include/access/relscan.h | 47 ++--
src/include/access/storageam.h | 32 +--
src/include/access/storageamapi.h | 23 +-
src/include/nodes/execnodes.h | 5 +-
39 files changed, 488 insertions(+), 380 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 063e07992b..55d2e09da5 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -56,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -71,7 +71,7 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index c4b10d6efc..32ac121e92 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -328,7 +329,8 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
- nblocks = scan->rs_nblocks; /* # blocks to be scanned */
+ pagescan = storageam_get_heappagescandesc(scan);
+ nblocks = pagescan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
@@ -364,7 +366,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -377,7 +379,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458ec91..14120291d0 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -221,14 +221,14 @@ system_rows_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -258,7 +258,7 @@ system_rows_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -291,7 +291,7 @@ system_rows_nextsampletuple(SampleScanState *node,
*/
/* We rely on the data accumulated in pagemode access */
- Assert(scan->rs_pageatatime);
+ Assert(pagescan->rs_pageatatime);
for (;;)
{
/* Advance to next possible offset on page */
@@ -308,7 +308,7 @@ system_rows_nextsampletuple(SampleScanState *node,
}
/* Found a candidate? */
- if (SampleOffsetVisible(tupoffset, scan))
+ if (SampleOffsetVisible(tupoffset, pagescan))
{
sampler->donetuples++;
break;
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025414..aa7252215a 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
instr_time cur_time;
/* First call within scan? */
@@ -232,14 +232,14 @@ system_time_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -275,7 +275,7 @@ system_time_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b64fec8e4a..9b5f24a650 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -107,8 +107,9 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_mo
* which tuples on the page are visible.
*/
void
-heapgetpage(HeapScanDesc scan, BlockNumber page)
+heapgetpage(StorageScanDesc sscan, BlockNumber page)
{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
Buffer buffer;
Snapshot snapshot;
Page dp;
@@ -118,13 +119,13 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
ItemId lpp;
bool all_visible;
- Assert(page < scan->rs_nblocks);
+ Assert(page < scan->rs_pagescan.rs_nblocks);
/* release previous scan buffer, if any */
- if (BufferIsValid(scan->rs_cbuf))
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
{
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
}
/*
@@ -135,20 +136,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
CHECK_FOR_INTERRUPTS();
/* read page using selected strategy */
- scan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM, page,
- RBM_NORMAL, scan->rs_strategy);
- scan->rs_cblock = page;
+ scan->rs_scan.rs_cbuf = ReadBufferExtended(scan->rs_scan.rs_rd, MAIN_FORKNUM, page,
+ RBM_NORMAL, scan->rs_pagescan.rs_strategy);
+ scan->rs_scan.rs_cblock = page;
- if (!scan->rs_pageatatime)
+ if (!scan->rs_pagescan.rs_pageatatime)
return;
- buffer = scan->rs_cbuf;
- snapshot = scan->rs_snapshot;
+ buffer = scan->rs_scan.rs_cbuf;
+ snapshot = scan->rs_scan.rs_snapshot;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
- heap_page_prune_opt(scan->rs_rd, buffer);
+ heap_page_prune_opt(scan->rs_scan.rs_rd, buffer);
/*
* We must hold share lock on the buffer content while examining tuple
@@ -158,7 +159,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
ntup = 0;
@@ -193,7 +194,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
HeapTupleData loctup;
bool valid;
- loctup.t_tableOid = RelationGetRelid(scan->rs_rd);
+ loctup.t_tableOid = RelationGetRelid(scan->rs_scan.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
@@ -201,20 +202,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, &loctup,
buffer, snapshot);
if (valid)
- scan->rs_vistuples[ntup++] = lineoff;
+ scan->rs_pagescan.rs_vistuples[ntup++] = lineoff;
}
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ scan->rs_pagescan.rs_ntuples = ntup;
}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 12a8f56456..a678402232 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1711,7 +1711,7 @@ retry:
else
{
SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ sync_startpage = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
goto retry;
}
}
@@ -1753,10 +1753,10 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* starting block number, modulo nblocks.
*/
nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
+ if (nallocated >= scan->rs_pagescan.rs_nblocks)
page = InvalidBlockNumber; /* all blocks have been allocated */
else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_pagescan.rs_nblocks;
/*
* Report scan location. Normally, we report the current page number.
@@ -1765,12 +1765,12 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* doesn't slew backwards. We only report the position at the end of the
* scan once, though: subsequent callers will report nothing.
*/
- if (scan->rs_syncscan)
+ if (scan->rs_pagescan.rs_syncscan)
{
if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ ss_report_location(scan->rs_scan.rs_rd, page);
+ else if (nallocated == scan->rs_pagescan.rs_nblocks)
+ ss_report_location(scan->rs_scan.rs_rd, parallel_scan->phs_startblock);
}
return page;
@@ -1799,9 +1799,9 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* lock that ensures the interesting tuple(s) won't change.)
*/
if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ scan->rs_pagescan.rs_nblocks = scan->rs_parallel->phs_nblocks;
else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+ scan->rs_pagescan.rs_nblocks = RelationGetNumberOfBlocks(scan->rs_scan.rs_rd);
/*
* If the table is large relative to NBuffers, use a bulk-read access
@@ -1815,8 +1815,8 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* Note that heap_parallelscan_initialize has a very similar test; if you
* change this, consider changing that one, too.
*/
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
+ if (!RelationUsesLocalBuffers(scan->rs_scan.rs_rd) &&
+ scan->rs_pagescan.rs_nblocks > NBuffers / 4)
{
allow_strat = scan->rs_allow_strat;
allow_sync = scan->rs_allow_sync;
@@ -1827,20 +1827,20 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (allow_strat)
{
/* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ if (scan->rs_pagescan.rs_strategy == NULL)
+ scan->rs_pagescan.rs_strategy = GetAccessStrategy(BAS_BULKREAD);
}
else
{
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
+ scan->rs_pagescan.rs_strategy = NULL;
}
if (scan->rs_parallel != NULL)
{
/* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ scan->rs_pagescan.rs_syncscan = scan->rs_parallel->phs_syncscan;
}
else if (keep_startblock)
{
@@ -1849,25 +1849,25 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ scan->rs_pagescan.rs_syncscan = (allow_sync && synchronize_seqscans);
}
else if (allow_sync && synchronize_seqscans)
{
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ scan->rs_pagescan.rs_syncscan = true;
+ scan->rs_pagescan.rs_startblock = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
}
else
{
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
+ scan->rs_pagescan.rs_syncscan = false;
+ scan->rs_pagescan.rs_startblock = 0;
}
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
+ scan->rs_pagescan.rs_numblocks = InvalidBlockNumber;
+ scan->rs_scan.rs_inited = false;
scan->rs_ctup.t_data = NULL;
ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
/* page-at-a-time fields are always invalid when not rs_inited */
@@ -1875,7 +1875,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* copy the scan key, if appropriate
*/
if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+ memcpy(scan->rs_scan.rs_key, key, scan->rs_scan.rs_nkeys * sizeof(ScanKeyData));
/*
* Currently, we don't have a stats counter for bitmap heap scans (but the
@@ -1883,7 +1883,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* update stats for tuple fetches there)
*/
if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
+ pgstat_count_heap_scan(scan->rs_scan.rs_rd);
}
@@ -1917,7 +1917,7 @@ heapgettup(HeapScanDesc scan,
ScanKey key)
{
HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
+ Snapshot snapshot = scan->rs_scan.rs_snapshot;
bool backward = ScanDirectionIsBackward(dir);
BlockNumber page;
bool finished;
@@ -1932,14 +1932,14 @@ heapgettup(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -1952,29 +1952,29 @@ heapgettup(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc)scan, page);
lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
/* page and lineoff now reference the physically next tid */
@@ -1985,14 +1985,14 @@ heapgettup(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2003,30 +2003,30 @@ heapgettup(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc)scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineoff = lines; /* final offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
@@ -2042,20 +2042,20 @@ heapgettup(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc)scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2086,21 +2086,21 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
tuple,
snapshot,
- scan->rs_cbuf);
+ scan->rs_scan.rs_cbuf);
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, tuple,
+ scan->rs_scan.rs_cbuf, snapshot);
if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -2125,17 +2125,17 @@ heapgettup(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* advance to next/prior page and detect end of scan
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2146,10 +2146,10 @@ heapgettup(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2163,8 +2163,8 @@ heapgettup(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2172,21 +2172,21 @@ heapgettup(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc)scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber((Page) dp);
linesleft = lines;
if (backward)
@@ -2237,14 +2237,14 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2257,27 +2257,27 @@ heapgettup_pagemode(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc)scan, page);
lineindex = 0;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
+ page = scan->rs_scan.rs_cblock; /* current page */
+ lineindex = scan->rs_pagescan.rs_cindex + 1;
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
/* page and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
@@ -2287,14 +2287,14 @@ heapgettup_pagemode(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2305,32 +2305,32 @@ heapgettup_pagemode(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc)scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineindex = lines - 1;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
- lineindex = scan->rs_cindex - 1;
+ lineindex = scan->rs_pagescan.rs_cindex - 1;
}
/* page and lineindex now reference the previous visible tid */
@@ -2341,20 +2341,20 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc)scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2363,8 +2363,8 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_len = ItemIdGetLength(lpp);
/* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+ Assert(scan->rs_pagescan.rs_cindex < scan->rs_pagescan.rs_ntuples);
+ Assert(lineoff == scan->rs_pagescan.rs_vistuples[scan->rs_pagescan.rs_cindex]);
return;
}
@@ -2377,7 +2377,7 @@ heapgettup_pagemode(HeapScanDesc scan,
{
while (linesleft > 0)
{
- lineoff = scan->rs_vistuples[lineindex];
+ lineoff = scan->rs_pagescan.rs_vistuples[lineindex];
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2392,17 +2392,17 @@ heapgettup_pagemode(HeapScanDesc scan,
{
bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
}
else
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
@@ -2422,10 +2422,10 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2436,10 +2436,10 @@ heapgettup_pagemode(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2453,8 +2453,8 @@ heapgettup_pagemode(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2462,20 +2462,20 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc)scan, page);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
linesleft = lines;
if (backward)
lineindex = lines - 1;
@@ -2485,7 +2485,7 @@ heapgettup_pagemode(HeapScanDesc scan,
}
-static HeapScanDesc
+static StorageScanDesc
heapam_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -2512,12 +2512,12 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
*/
scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
+ scan->rs_scan.rs_rd = relation;
+ scan->rs_scan.rs_snapshot = snapshot;
+ scan->rs_scan.rs_nkeys = nkeys;
scan->rs_bitmapscan = is_bitmapscan;
scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_pagescan.rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_temp_snap = temp_snap;
@@ -2526,7 +2526,7 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
*/
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
/*
* For a seqscan in a serializable transaction, acquire a predicate lock
@@ -2550,13 +2550,29 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* initscan() and we don't want to allocate memory again
*/
if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ scan->rs_scan.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
- scan->rs_key = NULL;
+ scan->rs_scan.rs_key = NULL;
initscan(scan, key, false);
- return scan;
+ return (StorageScanDesc)scan;
+}
+
+static ParallelHeapScanDesc
+heapam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
+
+ return scan->rs_parallel;
+}
+
+static HeapPageScanDesc
+heapam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc)sscan;
+
+ return &scan->rs_pagescan;
}
/* ----------------
@@ -2564,21 +2580,23 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* ----------------
*/
static void
-heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+heapam_rescan(StorageScanDesc sscan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
if (set_params)
{
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_scan.rs_snapshot);
}
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* reinitialize scan descriptor
@@ -2609,29 +2627,31 @@ heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
* ----------------
*/
static void
-heapam_endscan(HeapScanDesc scan)
+heapam_endscan(StorageScanDesc sscan)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* decrement relation reference count and free scan descriptor storage
*/
- RelationDecrementReferenceCount(scan->rs_rd);
+ RelationDecrementReferenceCount(scan->rs_scan.rs_rd);
- if (scan->rs_key)
- pfree(scan->rs_key);
+ if (scan->rs_scan.rs_key)
+ pfree(scan->rs_scan.rs_key);
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ UnregisterSnapshot(scan->rs_scan.rs_snapshot);
pfree(scan);
}
@@ -2643,12 +2663,14 @@ heapam_endscan(HeapScanDesc scan)
* ----------------
*/
static void
-heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+heapam_scan_update_snapshot(StorageScanDesc sscan, Snapshot snapshot)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(IsMVCCSnapshot(snapshot));
RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
+ scan->rs_scan.rs_snapshot = snapshot;
scan->rs_temp_snap = true;
}
@@ -2677,7 +2699,7 @@ heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
static StorageTuple
-heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+heapam_getnext(StorageScanDesc sscan, ScanDirection direction)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2685,11 +2707,11 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
HEAPAMDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2703,7 +2725,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
*/
HEAPAMDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return &(scan->rs_ctup);
}
@@ -2723,7 +2745,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
#endif
static TupleTableSlot *
-heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+heapam_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2731,11 +2753,11 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2750,11 +2772,32 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
*/
HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
slot, InvalidBuffer, true);
}
+static StorageTuple
+heapam_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+ Page dp;
+ ItemId lp;
+
+ dp = (Page) BufferGetPage(scan->rs_scan.rs_cbuf);
+ lp = PageGetItemId(dp, offset);
+ Assert(ItemIdIsNormal(lp));
+
+ scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+ scan->rs_ctup.t_len = ItemIdGetLength(lp);
+ scan->rs_ctup.t_tableOid = scan->rs_scan.rs_rd->rd_id;
+ ItemPointerSet(&scan->rs_ctup.t_self, blkno, offset);
+
+ pgstat_count_heap_fetch(scan->rs_scan.rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
/*
* heap_fetch - retrieve tuple with given tid
*
@@ -3069,18 +3112,18 @@ heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
static void
-heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+heapam_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+ Assert(!scan->rs_scan.rs_inited); /* else too late to change */
+ Assert(!scan->rs_pagescan.rs_syncscan); /* else rs_startblock is significant */
/* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+ Assert(startBlk == 0 || startBlk < scan->rs_pagescan.rs_nblocks);
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
+ scan->rs_pagescan.rs_startblock = startBlk;
+ scan->rs_pagescan.rs_numblocks = numBlks;
}
@@ -5086,12 +5129,26 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->scansetlimits = heapam_setscanlimits;
amroutine->scan_getnext = heapam_getnext;
amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_fetch_tuple_from_offset = heapam_fetch_tuple_from_offset;
amroutine->scan_end = heapam_endscan;
amroutine->scan_rescan = heapam_rescan;
amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
amroutine->tuple_freeze = heapam_freeze_tuple;
amroutine->hot_search_buffer = heapam_hot_search_buffer;
+ /*
+ * The following routine needs to be provided when the storage
+ * support parallel sequential scan
+ */
+ amroutine->scan_get_parallelheapscandesc = heapam_get_parallelheapscandesc;
+
+ /*
+ * The following routine needs to be provided when the storage
+ * support BitmapHeap and Sample Scans
+ */
+ amroutine->scan_get_heappagescandesc = heapam_get_heappagescandesc;
+
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 76b94dc8c3..af59d46a86 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -54,7 +54,7 @@
* Caller must hold a suitable lock on the correct relation.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
{
Snapshot snapshot;
@@ -67,6 +67,25 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
true, true, true, false, false, true);
}
+ParallelHeapScanDesc
+storageam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_get_parallelheapscandesc(sscan);
+}
+
+HeapPageScanDesc
+storageam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ /*
+ * Planner should have already validated whether the current storage
+ * supports Page scans are not? This function will be called only from
+ * Bitmap Heap scan and sample scan
+ */
+ Assert (sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc != NULL);
+
+ return sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc(sscan);
+}
+
/*
* heap_setscanlimits - restrict range of a heapscan
*
@@ -74,7 +93,7 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
}
@@ -104,7 +123,7 @@ storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numB
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -112,7 +131,7 @@ storage_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -122,7 +141,7 @@ storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -132,7 +151,7 @@ storage_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -140,7 +159,7 @@ storage_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -155,7 +174,7 @@ storage_beginscan_sampling(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-storage_rescan(HeapScanDesc scan,
+storage_rescan(StorageScanDesc scan,
ScanKey key)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
@@ -171,7 +190,7 @@ storage_rescan(HeapScanDesc scan,
* ----------------
*/
void
-storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
@@ -186,7 +205,7 @@ storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
* ----------------
*/
void
-storage_endscan(HeapScanDesc scan)
+storage_endscan(StorageScanDesc scan)
{
scan->rs_rd->rd_stamroutine->scan_end(scan);
}
@@ -199,23 +218,29 @@ storage_endscan(HeapScanDesc scan)
* ----------------
*/
void
-storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot)
{
scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
}
StorageTuple
-storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+storage_getnext(StorageScanDesc sscan, ScanDirection direction)
{
return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
}
TupleTableSlot*
-storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
}
+StorageTuple
+storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_fetch_tuple_from_offset(sscan, blkno, offset);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index db5c93b4c7..bcdef50366 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -478,10 +478,10 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
}
else
{
- HeapScanDesc scan = sysscan->scan;
+ StorageScanDesc scan = sysscan->scan;
Assert(IsMVCCSnapshot(scan->rs_snapshot));
- Assert(tup == &scan->rs_ctup);
+ //hari Assert(tup == &scan->rs_ctup);
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index e270cbc4a0..8793b95c08 100644
--- a/src/backend/access/tablesample/system.c
+++ b/src/backend/access/tablesample/system.c
@@ -183,7 +183,7 @@ static BlockNumber
system_nextsampleblock(SampleScanState *node)
{
SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc scan = node->pagescan;
BlockNumber nextblock = sampler->nextblock;
uint32 hashinput[2];
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 975cd5be99..17c1e1fef4 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -564,7 +564,7 @@ boot_openrel(char *relname)
int i;
struct typmap **app;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
@@ -880,7 +880,7 @@ gettype(char *type)
{
int i;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
struct typmap **app;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d2a8a06097..281e3a8e2a 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
{
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 68c46f720d..2564b14f19 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1897,7 +1897,7 @@ index_update_stats(Relation rel,
ReindexIsProcessingHeap(RelationRelationId))
{
/* don't assume syscache will work */
- HeapScanDesc pg_class_scan;
+ StorageScanDesc pg_class_scan;
ScanKeyData key[1];
ScanKeyInit(&key[0],
@@ -2210,7 +2210,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
{
bool is_system_catalog;
bool checking_uniqueness;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2649,7 +2649,7 @@ IndexCheckExclusion(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2963,7 +2963,7 @@ validate_index_heapscan(Relation heapRelation,
Snapshot snapshot,
v_i_state *state)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 415421917b..a8692c2580 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -947,7 +947,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
Snapshot snapshot;
TupleDesc tupdesc;
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 1d048e6394..842abcc8b5 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -151,7 +151,7 @@ RemoveConversionById(Oid conversionOid)
{
Relation rel;
HeapTuple tuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scanKeyData;
ScanKeyInit(&scanKeyData,
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 517e3101cd..63324cfc8e 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -171,7 +171,7 @@ void
DropSetting(Oid databaseid, Oid roleid)
{
Relation relsetting;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 145e3c1d65..5e3915b438 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index a51f2e4dfc..050dfa3e4c 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -377,7 +377,7 @@ void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData skey[2];
HeapTuple tup;
int nkeys = 0;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e0f6973a3f..ccdbe70ff6 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -746,7 +746,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
Datum *values;
bool *isnull;
IndexScanDesc indexScan;
- HeapScanDesc heapScan;
+ StorageScanDesc heapScan;
bool use_wal;
bool is_system_catalog;
TransactionId OldestXmin;
@@ -1638,7 +1638,7 @@ static List *
get_tables_to_cluster(MemoryContext cluster_context)
{
Relation indRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData entry;
HeapTuple indexTuple;
Form_pg_index index;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index be20ac7b2d..87c8d9ed07 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2022,7 +2022,7 @@ CopyTo(CopyState cstate)
{
Datum *values;
bool *nulls;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 39850b1b37..09135774c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -99,7 +99,7 @@ static int errdetail_busy_db(int notherbackends, int npreparedxacts);
Oid
createdb(ParseState *pstate, const CreatedbStmt *stmt)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
Relation rel;
Oid src_dboid;
Oid src_owner;
@@ -1872,7 +1872,7 @@ static void
remove_dbtablespaces(Oid db_id)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
@@ -1939,7 +1939,7 @@ check_db_file_conflict(Oid db_id)
{
bool result = false;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 46bc3da1fb..f8cbfcc33b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1875,7 +1875,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{
Oid objectOid;
Relation relationRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scan_keys[1];
HeapTuple tuple;
MemoryContext private_context;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cd86fb9101..2a077a3a1d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4481,7 +4481,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
@@ -5053,7 +5053,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
@@ -8202,7 +8202,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Expr *origexpr;
ExprState *exprstate;
TupleDesc tupdesc;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
MemoryContext oldcxt;
@@ -8285,7 +8285,7 @@ validateForeignKeyConstraint(char *conname,
Oid pkindOid,
Oid constraintOid)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
@@ -10737,7 +10737,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ListCell *l;
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index cdfa8ffb3f..be3c0db9e2 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -402,7 +402,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
char *tablespacename = stmt->tablespacename;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
Relation rel;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -913,7 +913,7 @@ RenameTableSpace(const char *oldname, const char *newname)
Oid tspId;
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
Form_pg_tablespace newform;
@@ -988,7 +988,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
{
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tup;
Oid tablespaceoid;
Datum datum;
@@ -1382,7 +1382,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
{
Oid result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -1428,7 +1428,7 @@ get_tablespace_name(Oid spc_oid)
{
char *result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c07f508d5b..09b5b9f422 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2310,7 +2310,7 @@ AlterDomainNotNull(List *names, bool notNull)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
@@ -2706,7 +2706,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index e24ac9f538..20f3a6cf8c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -443,7 +443,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
* pg_class
*/
Relation pgclass;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
@@ -1091,7 +1091,7 @@ vac_truncate_clog(TransactionId frozenXID,
{
TransactionId nextXID = ReadNewTransactionId();
Relation relation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid oldestxid_datoid;
Oid minmulti_datoid;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 7921025178..208ff91d4c 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -54,14 +54,14 @@
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
-static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
+static void bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres);
static inline void BitmapDoneInitializingSharedState(
ParallelBitmapHeapState *pstate);
static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node,
TBMIterateResult *tbmres);
static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
static inline void BitmapPrefetch(BitmapHeapScanState *node,
- HeapScanDesc scan);
+ StorageScanDesc scan);
static bool BitmapShouldInitializeSharedState(
ParallelBitmapHeapState *pstate);
@@ -76,7 +76,8 @@ static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
TIDBitmap *tbm;
TBMIterator *tbmiterator = NULL;
TBMSharedIterator *shared_tbmiterator = NULL;
@@ -92,6 +93,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scan = node->ss.ss_currentScanDesc;
+ pagescan = node->pagescan;
tbm = node->tbm;
if (pstate == NULL)
tbmiterator = node->tbmiterator;
@@ -191,8 +193,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
for (;;)
{
- Page dp;
- ItemId lp;
+ StorageTuple tuple;
CHECK_FOR_INTERRUPTS();
@@ -219,7 +220,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
- if (tbmres->blockno >= scan->rs_nblocks)
+ if (tbmres->blockno >= pagescan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
@@ -228,7 +229,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Fetch the current heap page and identify candidate tuples.
*/
- bitgetpage(scan, tbmres);
+ bitgetpage(node, tbmres);
if (tbmres->ntuples >= 0)
node->exact_pages++;
@@ -238,7 +239,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
- scan->rs_cindex = 0;
+ pagescan->rs_cindex = 0;
/* Adjust the prefetch target */
BitmapAdjustPrefetchTarget(node);
@@ -248,7 +249,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
- scan->rs_cindex++;
+ pagescan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -275,7 +276,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
- if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
+ if (pagescan->rs_cindex < 0 || pagescan->rs_cindex >= pagescan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
continue;
@@ -293,23 +294,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple
*/
- targoffset = scan->rs_vistuples[scan->rs_cindex];
- dp = (Page) BufferGetPage(scan->rs_cbuf);
- lp = PageGetItemId(dp, targoffset);
- Assert(ItemIdIsNormal(lp));
-
- scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- scan->rs_ctup.t_len = ItemIdGetLength(lp);
- scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
-
- pgstat_count_heap_fetch(scan->rs_rd);
+ targoffset = pagescan->rs_vistuples[pagescan->rs_cindex];
+ tuple = storage_fetch_tuple_from_offset(scan, tbmres->blockno, targoffset);
/*
* Set up the result slot to point to this tuple. Note that the slot
* acquires a pin on the buffer.
*/
- ExecStoreTuple(&scan->rs_ctup,
+ ExecStoreTuple(tuple,
slot,
scan->rs_cbuf,
false);
@@ -350,8 +342,10 @@ BitmapHeapNext(BitmapHeapScanState *node)
* interesting according to the bitmap, and visible according to the snapshot.
*/
static void
-bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
+bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres)
{
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
BlockNumber page = tbmres->blockno;
Buffer buffer;
Snapshot snapshot;
@@ -360,7 +354,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
- Assert(page < scan->rs_nblocks);
+ Assert(page < pagescan->rs_nblocks);
scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
scan->rs_rd,
@@ -403,7 +397,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
ItemPointerSet(&tid, page, offnum);
if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
- scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
+ pagescan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
@@ -419,23 +413,20 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
{
ItemId lp;
- HeapTupleData loctup;
+ StorageTuple loctup;
bool valid;
lp = PageGetItemId(dp, offnum);
if (!ItemIdIsNormal(lp))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- loctup.t_len = ItemIdGetLength(lp);
- loctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ loctup = storage_fetch_tuple_from_offset(scan, page, offnum);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, loctup, snapshot, buffer);
if (valid)
{
- scan->rs_vistuples[ntup++] = offnum;
- PredicateLockTuple(scan->rs_rd, &loctup, snapshot);
+ pagescan->rs_vistuples[ntup++] = offnum;
+ PredicateLockTuple(scan->rs_rd, loctup, snapshot);
}
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_rd, loctup,
buffer, snapshot);
}
}
@@ -443,7 +434,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ pagescan->rs_ntuples = ntup;
}
/*
@@ -569,7 +560,7 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
* BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
*/
static inline void
-BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
+BitmapPrefetch(BitmapHeapScanState *node, StorageScanDesc scan)
{
#ifdef USE_PREFETCH
ParallelBitmapHeapState *pstate = node->pstate;
@@ -724,7 +715,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -871,6 +862,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
0,
NULL);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
+
/*
* get the scan type from the relation descriptor.
*/
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 04f85e5f2d..589505a509 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -31,7 +31,7 @@ static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
static StorageTuple tablesample_getnext(SampleScanState *scanstate);
static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan); //hari
+ SampleScanState *scanstate);
/* ----------------------------------------------------------------
* Scan Support
@@ -66,8 +66,7 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
- InvalidBuffer,
+ node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -356,6 +355,7 @@ tablesample_init(SampleScanState *scanstate)
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
}
else
{
@@ -381,10 +381,11 @@ static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+ StorageTuple tuple;
Snapshot snapshot = scan->rs_snapshot;
- bool pagemode = scan->rs_pageatatime;
+ bool pagemode = pagescan->rs_pageatatime;
BlockNumber blockno;
Page page;
bool all_visible;
@@ -395,10 +396,9 @@ tablesample_getnext(SampleScanState *scanstate)
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
{
Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
return NULL;
}
if (tsm->NextSampleBlock)
@@ -406,13 +406,12 @@ tablesample_getnext(SampleScanState *scanstate)
blockno = tsm->NextSampleBlock(scanstate);
if (!BlockNumberIsValid(blockno))
{
- tuple->t_data = NULL;
return NULL;
}
}
else
- blockno = scan->rs_startblock;
- Assert(blockno < scan->rs_nblocks);
+ blockno = pagescan->rs_startblock;
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
scan->rs_inited = true;
}
@@ -455,14 +454,12 @@ tablesample_getnext(SampleScanState *scanstate)
if (!ItemIdIsNormal(itemid))
continue;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
- tuple->t_len = ItemIdGetLength(itemid);
- ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
+ tuple = storage_fetch_tuple_from_offset(scan, blockno, tupoffset);
if (all_visible)
visible = true;
else
- visible = SampleTupleVisible(tuple, tupoffset, scan);
+ visible = SampleTupleVisible(tuple, tupoffset, scanstate);
/* in pagemode, heapgetpage did this for us */
if (!pagemode)
@@ -493,14 +490,14 @@ tablesample_getnext(SampleScanState *scanstate)
if (tsm->NextSampleBlock)
{
blockno = tsm->NextSampleBlock(scanstate);
- Assert(!scan->rs_syncscan);
+ Assert(!pagescan->rs_syncscan);
finished = !BlockNumberIsValid(blockno);
}
else
{
/* Without NextSampleBlock, just do a plain forward seqscan. */
blockno++;
- if (blockno >= scan->rs_nblocks)
+ if (blockno >= pagescan->rs_nblocks)
blockno = 0;
/*
@@ -513,10 +510,10 @@ tablesample_getnext(SampleScanState *scanstate)
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
+ if (pagescan->rs_syncscan)
ss_report_location(scan->rs_rd, blockno);
- finished = (blockno == scan->rs_startblock);
+ finished = (blockno == pagescan->rs_startblock);
}
/*
@@ -528,12 +525,11 @@ tablesample_getnext(SampleScanState *scanstate)
ReleaseBuffer(scan->rs_cbuf);
scan->rs_cbuf = InvalidBuffer;
scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
scan->rs_inited = false;
return NULL;
}
- Assert(blockno < scan->rs_nblocks);
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
/* Re-establish state for new page */
@@ -548,16 +544,19 @@ tablesample_getnext(SampleScanState *scanstate)
/* Count successfully-fetched tuples as heap fetches */
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return tuple;
}
/*
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, SampleScanState *scanstate)
{
- if (scan->rs_pageatatime)
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+
+ if (pagescan->rs_pageatatime)
{
/*
* In pageatatime mode, heapgetpage() already did visibility checks,
@@ -569,12 +568,12 @@ SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan
* gain to justify the restriction.
*/
int start = 0,
- end = scan->rs_ntuples - 1;
+ end = pagescan->rs_ntuples - 1;
while (start <= end)
{
int mid = (start + end) / 2;
- OffsetNumber curoffset = scan->rs_vistuples[mid];
+ OffsetNumber curoffset = pagescan->rs_vistuples[mid];
if (tupoffset == curoffset)
return true;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 839d3a65ec..ec6702484f 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -316,9 +316,10 @@ void
ExecSeqScanReInitializeDSM(SeqScanState *node,
ParallelContext *pcxt)
{
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ ParallelHeapScanDesc pscan;
- heap_parallelscan_reinitialize(scan->rs_parallel);
+ pscan = storageam_get_parallelheapscandesc(node->ss.ss_currentScanDesc);
+ heap_parallelscan_reinitialize(pscan);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a1ebd4acc8..4503593ffc 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -248,7 +248,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amsearchnulls = amroutine->amsearchnulls;
info->amcanparallel = amroutine->amcanparallel;
info->amhasgettuple = (amroutine->amgettuple != NULL);
- info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amhasgetbitmap = ((amroutine->amgetbitmap != NULL)
+ && (relation->rd_stamroutine->scan_get_heappagescandesc != NULL));
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index fec203dee3..9f505a5085 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1864,7 +1864,7 @@ get_database_list(void)
{
List *dblist = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
@@ -1930,7 +1930,7 @@ do_autovacuum(void)
{
Relation classRel;
HeapTuple tuple;
- HeapScanDesc relScan;
+ StorageScanDesc relScan;
Form_pg_database dbForm;
List *table_oids = NIL;
List *orphan_oids = NIL;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 74113a75b8..86db35d790 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1207,7 +1207,7 @@ pgstat_collect_oids(Oid catalogid)
HTAB *htab;
HASHCTL hash_ctl;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 61981a0939..13d84a75a8 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -107,7 +107,7 @@ get_subscription_list(void)
{
List *res = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4924daca76..7122987ad7 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -420,7 +420,7 @@ DefineQueryRewrite(char *rulename,
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
Snapshot snapshot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 03b7cc76d7..42a35824e5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1208,7 +1208,7 @@ static bool
ThereIsAtLeastOneRole(void)
{
Relation pg_authid_rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
bool result;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4cddd73355..42c26861de 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -97,6 +97,8 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
#define heap_close(r,l) relation_close(r,l)
/* struct definitions appear in relscan.h */
+typedef struct HeapPageScanDescData *HeapPageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
@@ -106,7 +108,7 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
+extern void heapgetpage(StorageScanDesc sscan, BlockNumber page);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 147f862a2b..8e00c842e9 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/htup_details.h"
#include "access/itup.h"
#include "access/tupdesc.h"
@@ -42,40 +43,54 @@ typedef struct ParallelHeapScanDescData
char phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelHeapScanDescData;
-typedef struct HeapScanDescData
+typedef struct StorageScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
Snapshot rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
ScanKey rs_key; /* array of scan key descriptors */
- bool rs_bitmapscan; /* true if this is really a bitmap scan */
- bool rs_samplescan; /* true if this is really a sample scan */
+
+ /* scan current state */
+ bool rs_inited; /* false = scan not init'd yet */
+ BlockNumber rs_cblock; /* current block # in scan, if any */
+ Buffer rs_cbuf; /* current buffer in scan, if any */
+} StorageScanDescData;
+
+typedef struct HeapPageScanDescData
+{
bool rs_pageatatime; /* verify visibility page-at-a-time? */
- bool rs_allow_strat; /* allow or disallow use of access strategy */
- bool rs_allow_sync; /* allow or disallow use of syncscan */
- bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* total number of blocks in rel */
BlockNumber rs_startblock; /* block # to start at */
BlockNumber rs_numblocks; /* max number of blocks to scan */
+
/* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */
BufferAccessStrategy rs_strategy; /* access strategy for reads */
bool rs_syncscan; /* report location to syncscan logic? */
- /* scan current state */
- bool rs_inited; /* false = scan not init'd yet */
- HeapTupleData rs_ctup; /* current tuple in scan, if any */
- BlockNumber rs_cblock; /* current block # in scan, if any */
- Buffer rs_cbuf; /* current buffer in scan, if any */
- /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- ParallelHeapScanDesc rs_parallel; /* parallel scan information */
-
/* these fields only used in page-at-a-time mode and for bitmap scans */
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
+} HeapPageScanDescData;
+
+typedef struct HeapScanDescData
+{
+ /* scan parameters */
+ StorageScanDescData rs_scan; /* */
+ HeapPageScanDescData rs_pagescan;
+ bool rs_bitmapscan; /* true if this is really a bitmap scan */
+ bool rs_samplescan; /* true if this is really a sample scan */
+ bool rs_allow_strat; /* allow or disallow use of access strategy */
+ bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
+
+ HeapTupleData rs_ctup; /* current tuple in scan, if any */
+
+ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+ ParallelHeapScanDesc rs_parallel; /* parallel scan information */
} HeapScanDescData;
/*
@@ -149,12 +164,12 @@ typedef struct ParallelIndexScanDescData
char ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelIndexScanDescData;
-/* Struct for heap-or-index scans of system tables */
+/* Struct for storage-or-index scans of system tables */
typedef struct SysScanDescData
{
Relation heap_rel; /* catalog being scanned */
Relation irel; /* NULL if doing heap scan */
- HeapScanDesc scan; /* only valid in heap-scan case */
+ StorageScanDesc scan; /* only valid in storage-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 507af714a9..f335c79ad1 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,7 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
-typedef void *StorageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef union tuple_data
{
@@ -37,29 +37,31 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
-extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
-
-extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
-extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+extern ParallelHeapScanDesc storageam_get_parallelheapscandesc(StorageScanDesc sscan);
+extern HeapPageScanDesc storageam_get_heappagescandesc(StorageScanDesc sscan);
+extern void storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern StorageScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync);
-extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_endscan(HeapScanDesc scan);
-extern void storage_rescan(HeapScanDesc scan, ScanKey key);
-extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern void storage_endscan(StorageScanDesc scan);
+extern void storage_rescan(StorageScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
-extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
-extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_getnext(StorageScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset);
extern void storage_get_latest_tid(Relation relation,
Snapshot snapshot,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index c2e6dc2aef..d849b86546 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -20,7 +20,7 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
-typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+typedef StorageScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -30,22 +30,26 @@ typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
bool is_bitmapscan,
bool is_samplescan,
bool temp_snap);
-typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+typedef ParallelHeapScanDesc (*scan_get_parallelheapscandesc_hook) (StorageScanDesc scan);
+typedef HeapPageScanDesc (*scan_get_heappagescandesc_hook) (StorageScanDesc scan);
+typedef void (*scan_setlimits_hook) (StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
/* must return a TupleTableSlot? */
-typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+typedef StorageTuple (*scan_getnext_hook) (StorageScanDesc scan,
ScanDirection direction);
-typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (StorageScanDesc scan,
ScanDirection direction, TupleTableSlot *slot);
+typedef StorageTuple (*scan_fetch_tuple_from_offset_hook) (StorageScanDesc scan,
+ BlockNumber blkno, OffsetNumber offset);
-typedef void (*scan_end_hook) (HeapScanDesc scan);
+typedef void (*scan_end_hook) (StorageScanDesc scan);
-typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
-typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+typedef void (*scan_getpage_hook) (StorageScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+typedef void (*scan_update_snapshot_hook) (StorageScanDesc scan, Snapshot snapshot);
typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
@@ -161,9 +165,12 @@ typedef struct StorageAmRoutine
/* Operations on relation scans */
scan_begin_hook scan_begin;
+ scan_get_parallelheapscandesc_hook scan_get_parallelheapscandesc;
+ scan_get_heappagescandesc_hook scan_get_heappagescandesc;
scan_setlimits_hook scansetlimits;
scan_getnext_hook scan_getnext;
scan_getnext_slot_hook scan_getnextslot;
+ scan_fetch_tuple_from_offset_hook scan_fetch_tuple_from_offset;
scan_end_hook scan_end;
scan_getpage_hook scan_getpage;
scan_rescan_hook scan_rescan;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c19698089b..a0fb626f36 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tupconvert.h"
#include "executor/instrument.h"
#include "lib/pairingheap.h"
@@ -1099,7 +1100,7 @@ typedef struct ScanState
{
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
- HeapScanDesc ss_currentScanDesc;
+ StorageScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
@@ -1120,6 +1121,7 @@ typedef struct SeqScanState
typedef struct SampleScanState
{
ScanState ss;
+ HeapPageScanDesc pagescan;
List *args; /* expr states for TABLESAMPLE params */
ExprState *repeatable; /* expr state for REPEATABLE expr */
/* use struct pointer to avoid including tsmapi.h here */
@@ -1344,6 +1346,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
+ HeapPageScanDesc pagescan;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
--
2.14.1.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.7K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/4-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From d31f37bb0b2ece2884ee71d91c8b51802f9197f6 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/8] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 4 ++++
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa0fd..33079c1c16 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818c9b..d4ac340a54 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5172,16 +5174,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..0a7e0a33e8 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b894..2c3e33c104 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
@@ -83,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f73c6c6201..6b675b0454 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3879,6 +3879,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..ea352fa247 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..3bc960741b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1711,11 +1711,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 2945966c0e..f1f58a3efc 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1153,15 +1153,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.14.1.windows.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.4K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/5-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From e2f36e838c7aea7112999766383fb6196b1b7b72 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/8] Storage AM API hooks and related functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 237 +++++++++++++++++++++++++++++++
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
8 files changed, 427 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496bcd..02a3909e7c 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000000..88827e7957
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000000..def2029287
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine *HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68639..6459435c78 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000000..95fe02888f
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,237 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*scan_setlimits_hook) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple (*scan_getnext_hook) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot* (*scan_getnext_slot_hook) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*scan_end_hook) (HeapScanDesc scan);
+
+
+typedef void (*scan_getpage_hook) (HeapScanDesc scan, BlockNumber page);
+typedef void (*scan_rescan_hook) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*scan_update_snapshot_hook) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*tuple_insert_hook) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*tuple_delete_hook) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*tuple_update_hook) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*tuple_fetch_hook) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*tuple_lock_hook) (Relation relation,
+ ItemPointer tid,
+ StorageTuple *tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*multi_insert_hook) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*tuple_freeze_hook) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*tuple_get_latest_tid_hook) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data (*get_tuple_data_hook) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple (*tuple_from_datum_hook) (Datum data, Oid tableoid);
+
+typedef bool (*tuple_is_heaponly_hook) (StorageTuple tuple);
+
+typedef void (*slot_store_tuple_hook) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*slot_clear_tuple_hook) (TupleTableSlot *slot);
+typedef Datum (*slot_getattr_hook) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*slot_virtualize_tuple_hook) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*slot_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*slot_min_tuple_hook) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*slot_update_tableoid_hook) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*speculative_finish_hook) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*speculative_abort_hook) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*relation_sync_hook) (Relation relation);
+
+typedef bool (*snapshot_satisfies_hook) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*snapshot_satisfies_update_hook) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*snapshot_satisfies_vacuum_hook) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ slot_store_tuple_hook slot_store_tuple;
+ slot_virtualize_tuple_hook slot_virtualize_tuple;
+ slot_clear_tuple_hook slot_clear_tuple;
+ slot_getattr_hook slot_getattr;
+ slot_tuple_hook slot_tuple;
+ slot_min_tuple_hook slot_min_tuple;
+ slot_update_tableoid_hook slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine* (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ scan_begin_hook scan_begin;
+ scan_setlimits_hook scansetlimits;
+ scan_getnext_hook scan_getnext;
+ scan_getnext_slot_hook scan_getnextslot;
+ scan_end_hook scan_end;
+ scan_getpage_hook scan_getpage;
+ scan_rescan_hook scan_rescan;
+ scan_update_snapshot_hook scan_update_snapshot;
+ hot_search_buffer_hook hot_search_buffer; /* heap_hot_search_buffer */
+
+ // heap_sync_function heap_sync; /* heap_sync */
+ /* not implemented */
+ // parallelscan_estimate_function parallelscan_estimate; /* heap_parallelscan_estimate */
+ // parallelscan_initialize_function parallelscan_initialize; /* heap_parallelscan_initialize */
+ // parallelscan_begin_function parallelscan_begin; /* heap_beginscan_parallel */
+
+ /* Operations on physical tuples */
+ tuple_insert_hook tuple_insert; /* heap_insert */
+ tuple_update_hook tuple_update; /* heap_update */
+ tuple_delete_hook tuple_delete; /* heap_delete */
+ tuple_fetch_hook tuple_fetch; /* heap_fetch */
+ tuple_lock_hook tuple_lock; /* heap_lock_tuple */
+ multi_insert_hook multi_insert; /* heap_multi_insert */
+ tuple_freeze_hook tuple_freeze; /* heap_freeze_tuple */
+ tuple_get_latest_tid_hook tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ get_tuple_data_hook get_tuple_data;
+ tuple_is_heaponly_hook tuple_is_heaponly;
+ tuple_from_datum_hook tuple_from_datum;
+
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no need
+ * for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ speculative_finish_hook speculative_finish;
+ speculative_abort_hook speculative_abort;
+
+
+ relation_sync_hook relation_sync; /* heap_sync */
+
+ snapshot_satisfies_hook snapshot_satisfies[END_OF_VISIBILITY];
+ snapshot_satisfies_update_hook snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ snapshot_satisfies_vacuum_hook snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine *GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine *GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine *GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6b675b0454..1dfb711be2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -558,6 +558,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 27bd4f3363..108df9b757 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -496,6 +496,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 9a3b56e5f0..de75e01706 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
2.14.1.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/6-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From 6f73928b1d1f0c1ae96a8455baeb89e947eac228 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/8] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 117 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..40cbc677c7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,26 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1885,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2026,6 +2108,11 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
RelationInitPhysicalAddr(relation);
+ /*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
/*
* initialize the rel-has-index flag, using hardwired knowledge
*/
@@ -2354,6 +2441,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3457,13 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3985,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert (relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5715,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5380..82d8a534b1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -160,6 +160,12 @@ typedef struct RelationData
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
+ /*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
/*
* index access support info (used only for an index relation)
*
@@ -427,6 +433,12 @@ typedef struct ViewOptions
*/
#define RelationGetDescr(relation) ((relation)->rd_att)
+/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
/*
* RelationGetRelationName
* Returns the rel's name.
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cefe4b..03d996f5a1 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.14.1.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (145.9K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/7-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From 289b45e52c1bb2e18baae4c63d43b2cad9280f40 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:38:28 +1000
Subject: [PATCH 4/8] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 10 +-
contrib/pgrowlocks/pgrowlocks.c | 3 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 23 +-
src/backend/access/heap/heapam_common.c | 162 +++
src/backend/access/heap/heapam_storage.c | 1622 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 4 +-
src/backend/commands/analyze.c | 2 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 7 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1809 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/access/htup.h | 3 +-
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 66 +-
29 files changed, 1964 insertions(+), 1907 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575d9f..10d47cd46f 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -51,7 +51,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +656,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +681,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +739,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65bd2..5f076efe7c 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,7 +150,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
GetCurrentCommandId(false),
scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf06138a5..284eabc970 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7ca1bb24d2..e098202f84 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909e7c..e6bc18e5ea 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f0381f3..c21d6f8559 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -1950,7 +1953,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2097,7 +2100,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2271,7 +2274,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3095,7 +3098,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3206,7 +3209,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3666,7 +3669,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3847,7 +3850,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4598,7 +4601,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000000..502f6dbccb
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
+
+
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 88827e7957..1bd4bfaa33 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,6 +48,1617 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
+ */
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
+ Buffer buffer)
+{
+ return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+ != HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except for known-committed
+ * XIDs which could not be ours anyway.
+ */
+static bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple)stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
@@ -54,5 +1666,15 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
+ amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
+ amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
+ amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
+ amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
+ amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] = HeapTupleSatisfiesHistoricMVCC;
+ amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] = HeapTupleSatisfiesNonVacuumable;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac417..74250688a8 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index bd560e47e1..191f088703 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da001a..01321a2543 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..0240df7a08 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,7 +2359,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 08fc18e96b..2611cca6d4 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1069,7 +1069,7 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
OldestXmin,
targbuffer))
{
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e2ad..dbcc5bc172 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 45b1859475..dccda5ab32 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -975,7 +975,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2140,7 +2140,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index f7e55e0b45..60a6cb03d8 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -428,7 +428,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 845c409540..b39e48b222 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1312,7 +1313,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a836e4..6a118d1883 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index fba57a0470..095d1edd12 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 251a359bff..4fbad9f0f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..5a6d2168e1 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa4c8..f17b1c5324 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 294ab705f1..f823da09f4 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2040,7 +2040,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2137,7 +2137,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index bbac4083c9..0000000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1809 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-/* local functions */
-static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleSatisfiesNonVacuumable
- *
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
- *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
- */
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
- != HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except for known-committed
- * XIDs which could not be ours anyway.
- */
-static bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000000..ff63cf3615
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6459435c78..5b0068a907 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -29,11 +29,12 @@ typedef MinimalTupleData *MinimalTuple;
typedef enum tuple_visibility_type
{
MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
- SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
END_OF_VISIBILITY
} tuple_visibility_type;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc5ba..b8b823bce9 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf519778df..5752ee4731 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -52,7 +52,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index de75e01706..67a8b1ea98 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,48 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
-
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies[(snapshot)->visibility_type]) (tuple, snapshot, buffer))
/*
* We don't provide a static SnapshotDirty variable because it would be
@@ -91,14 +52,14 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -106,8 +67,19 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
2.14.1.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (60.1K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/8-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 441b8930f0467e50a928329a6b9889c487408e0f Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 17:24:35 +1000
Subject: [PATCH 5/8] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 1 +
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 15 +-
src/backend/executor/execExprInterp.c | 26 +-
src/backend/executor/execReplication.c | 92 ++-----
src/backend/executor/execTuples.c | 269 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 644 insertions(+), 575 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528e26..5ed0f15ac4 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1021,111 +1022,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
}
-/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 502f6dbccb..6cb6c5be30 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -159,4 +159,408 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *)slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *)palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple)tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple)tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine*
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 1bd4bfaa33..7d7ac759e3 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1665,6 +1665,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->slot_storageam = heapam_storage_slot_handler;
amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c6fa44563c..f1f546a321 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2697,7 +2697,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e60210cb24..a0ec444d33 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376511..b440740e28 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index e75a59d299..2f530169b8 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2289,7 +2289,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2370,7 +2370,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2728,7 +2728,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2770,7 +2770,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2879,7 +2879,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -4013,14 +4013,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index bd8a15d6c3..47757386ee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -503,12 +503,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+ //hari Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(innerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -519,12 +519,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
-
+ Assert(outerslot->tts_storage != NULL);
+ //hari Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
+
+ *op->resvalue = slot_getattr(outerslot,
+ attnum,
op->resnull);
EEO_NEXT();
@@ -535,12 +535,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
int attnum = op->d.var.attnum;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+ //hari Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
/* heap_getsysattr has sufficient defenses against bad attnums */
- *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
+ *op->resvalue = slot_getattr(scanslot,
+ attnum,
op->resnull);
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 5a75e0211f..6700f0ad80 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,7 +226,8 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ TupleTableSlot *scanslot;
+ HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
TransactionId xwait;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d166..b7a2cbc023 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -205,6 +205,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
return slot;
}
+/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert (slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,18 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
-
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +459,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +540,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +549,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +568,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +598,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +645,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +653,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +664,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +697,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +717,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,18 +728,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
@@ -768,17 +741,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6d5f..02f6c816aa 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b39e48b222..dd8f792404 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -406,7 +406,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -419,7 +419,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -437,7 +437,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -746,7 +746,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -897,7 +897,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -958,7 +958,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -977,7 +977,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -991,7 +991,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1007,7 +1007,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1123,7 +1123,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index e9a5d5a1a5..9a47276274 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -58,7 +58,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
shm_mq_result result;
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index bc6d8246a7..a60be0049d 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -724,9 +724,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3615..1fe15ede56 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index fa04a63b76..9539d67bec 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -20,6 +20,19 @@
#include "access/transam.h"
#include "storage/bufpage.h"
+/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
@@ -653,7 +666,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce4ee..84dc293122 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.14.1.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (246.2K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/9-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From 58e0f867076b3ea20cc4aab1c2bebb50e040a6b5 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Thu, 7 Sep 2017 14:48:24 +1000
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM.
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2737 ++++--------------------------
src/backend/access/heap/heapam_storage.c | 2153 ++++++++++++++++++++++-
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 306 ++++
src/backend/access/heap/tuptoaster.c | 8 +-
src/backend/commands/copy.c | 29 +-
src/backend/commands/createas.c | 18 +-
src/backend/commands/matview.c | 10 +-
src/backend/commands/tablecmds.c | 5 +-
src/backend/commands/trigger.c | 43 +-
src/backend/executor/execMain.c | 120 +-
src/backend/executor/execReplication.c | 35 +-
src/backend/executor/nodeLockRows.c | 39 +-
src/backend/executor/nodeModifyTable.c | 177 +-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/htup_details.h | 1 +
src/include/access/storageam.h | 81 +
src/include/access/storageamapi.h | 21 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 6 +-
src/include/nodes/execnodes.h | 4 +-
26 files changed, 3290 insertions(+), 2717 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 5ed0f15ac4..714c4d862c 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
return PointerGetDatum(td);
}
+/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e5ea..162736ff15 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index c21d6f8559..d20f211a08 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1837,158 +1744,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
return &(scan->rs_ctup);
}
-/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
@@ -2172,130 +1927,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -2313,7 +1944,7 @@ heap_get_latest_tid(Relation relation,
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2596,7 +2227,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2664,412 +2295,110 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
-
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ Assert(ItemPointerIsValid(tid));
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
*/
if (PageIsAllVisible(page))
visibilitymap_pin(relation, block, &vmbuffer);
@@ -4504,7 +3833,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4522,724 +3851,34 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
-
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
+ if (*have_tuple_lock)
+ return true;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode))
@@ -5272,7 +3911,7 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
* window, but it's still possible to end up creating an unnecessary
* MultiXactId. Fortunately this is harmless.
*/
-static void
+void
compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
uint16 old_infomask2, TransactionId add_to_xmax,
LockTupleMode mode, bool is_update,
@@ -5319,916 +3958,226 @@ l5:
break;
case LockTupleNoKeyExclusive:
new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
- }
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
-}
-
-/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
- *
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
- *
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
- *
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
- */
-void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
-{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
-
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
-
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
+}
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
-}
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
@@ -6993,7 +4942,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7160,7 +5109,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7182,7 +5131,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7744,7 +5693,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9120,46 +7069,6 @@ heap2_redo(XLogReaderState *record)
}
}
-/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
/*
* Mask a heap page before performing consistency checks on it.
*/
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 7d7ac759e3..a0e3272f67 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -50,7 +50,13 @@
/* local functions */
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
/*-------------------------------------------------------------------------
*
* POSTGRES "time qualification" code, ie, tuple visibility rules.
@@ -1660,10 +1666,2149 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
-Datum
-heapam_storage_handler(PG_FUNCTION_ARGS)
+
+/*
+ * heap_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
{
- StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple->t_self));
+ tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple->t_len = ItemIdGetLength(lp);
+ tuple->t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is completely
+ * bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ /*Assert(slot->tts_speculativeToken != 0); This needs some update in toast */
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTupleData mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ priorXmax))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup.t_data->t_infomask;
+ old_infomask2 = mytup.t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup.t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup.t_data->t_infomask |= new_infomask;
+ mytup.t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data)HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data)HeapTupleHeaderGetUpdateXid(((HeapTuple)tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data)HeapTupleHeaderGetCmin(((HeapTuple)tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data)((HeapTuple)tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data)((HeapTuple)tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static bool
+heapam_tuple_is_heaopnly(StorageTuple tuple)
+{
+ return HeapTupleIsHeapOnly((HeapTuple)tuple);
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_finish = heapam_finish_speculative;
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_is_heaponly = heapam_tuple_is_heaopnly;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
amroutine->slot_storageam = heapam_storage_slot_handler;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 191f088703..8fba61c4f1 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000000..d1d7364e7f
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,306 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid,
+ options, bistate);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, cid,
+ crosscheck, wait, hufd, lockmode);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+
+/*
+ * storage_finish_speculative - mark speculative insertion as successful
+ *
+ * To successfully finish a speculative insertion we have to clear speculative
+ * token from tuple. To do so the t_ctid field, which will contain a
+ * speculative token value, is modified in place to point to the tuple itself,
+ * which is characteristic of a newly inserted ordinary tuple.
+ *
+ * NB: It is not ok to commit without either finishing or aborting a
+ * speculative insertion. We could treat speculative tuples of committed
+ * transactions implicitly as completed, but then we would have to be prepared
+ * to deal with speculative tokens on committed tuples. That wouldn't be
+ * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
+ * but clearing the token at completion isn't very expensive either.
+ * An explicit confirmation WAL record also makes logical decoding simpler.
+ */
+void
+storage_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_finish(relation, slot);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+bool
+storage_tuple_is_heaponly(Relation relation, StorageTuple tuple)
+{
+ return relation->rd_stamroutine->tuple_is_heaponly(tuple);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..d766a6eb6a 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,12 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f1f546a321..beb7f050fe 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2696,8 +2697,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2760,19 +2759,18 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, bistate);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
+ &(slot->tts_tid),
estate,
false,
NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2868,7 +2866,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2901,12 +2899,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2925,7 +2923,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2942,8 +2940,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index a0ec444d33..d119149039 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,24 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ slot->tts_tupleOid = InvalidOid;
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +619,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740e28..6102481fde 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,16 +492,15 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
- heap_insert(myState->transientrel,
- tuple,
+ storage_insert(myState->transientrel,
+ slot,
myState->output_cid,
myState->hi_options,
myState->bistate);
@@ -522,7 +522,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 563bcda30c..c9e5ae0832 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4652,7 +4653,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate);
ResetExprContext(econtext);
@@ -4676,7 +4677,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 2f530169b8..8e2f351949 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2352,17 +2352,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3012,9 +3016,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3030,11 +3035,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, &tuple,
estate->es_output_cid,
lockmode, LockWaitBlock,
false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3066,7 +3071,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3112,6 +3118,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3130,17 +3137,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3953,8 +3960,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -4027,10 +4034,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -4044,10 +4050,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 62fb05efac..8657a139ed 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1895,7 +1895,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1975,7 +1975,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2022,7 +2022,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2481,7 +2481,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2498,7 +2499,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2529,7 +2532,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2562,14 +2565,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2577,12 +2580,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2596,8 +2599,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2618,7 +2621,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2647,17 +2651,20 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
+ test = storage_lock_tuple(relation, tid, tuple,
estate->es_output_cid,
lockmode, wait_policy,
false, &buffer, &hufd);
@@ -2696,12 +2703,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2723,10 +2733,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2735,7 +2741,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2744,8 +2750,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
- priorXmax))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (!TransactionIdEquals(t_data.xid, priorXmax))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2763,7 +2769,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2771,17 +2779,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2827,7 +2837,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2846,7 +2856,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2874,7 +2884,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2905,8 +2915,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2930,11 +2938,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
erm,
datum,
&updated);
- if (copyTuple == NULL)
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2948,23 +2956,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2974,19 +2977,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3152,8 +3148,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 6700f0ad80..8d625b6cbe 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
lockmode,
LockWaitBlock,
false /* don't follow updates */ ,
&buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,7 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -349,6 +351,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ tuple_data t_data;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
@@ -359,14 +362,15 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
+ t_data = storage_tuple_get_data(rel, tuple, TID);
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(t_data.tid),
estate, false, NULL,
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +394,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -426,8 +430,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* OK, update the tuple and index entries for it */
simple_heap_update(rel, tid, tuple);
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
+ if (resultRelInfo->ri_NumIndices > 0 && !storage_tuple_is_heaponly(rel, tuple))
recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..f06f34a1ec 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple)(&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
estate->es_output_cid,
lockmode, erm->waitPolicy, true,
&buffer, &hufd);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index dd8f792404..4681dc8dac 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
+ Buffer buffer;
Relation rel = relinfo->ri_RelationDesc;
- Buffer buffer;
- HeapTupleData tuple;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -267,12 +271,6 @@ ExecInsert(ModifyTableState *mtstate,
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
/*
* get information on the (current) result relation
*/
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -374,19 +374,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where the
+ * plan tree can return a tuple extracted literally from some table with
+ * the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -404,9 +416,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -418,9 +427,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -436,14 +442,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -463,7 +467,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -554,24 +559,24 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
HEAP_INSERT_SPECULATIVE,
NULL);
/* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, true, &specConflict,
arbiterIndexes);
/* adjust the tuple's state accordingly */
if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
+ storage_finish_speculative(resultRelationDesc, slot);
else
- heap_abort_speculative(resultRelationDesc, tuple);
+ storage_abort_speculative(resultRelationDesc, slot);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -599,17 +604,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
+ newId = storage_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL,
arbiterIndexes);
}
@@ -619,11 +621,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -674,7 +676,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -719,8 +721,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -746,8 +746,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -761,7 +763,7 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
+ result = storage_delete(resultRelationDesc, tupleid,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -861,7 +863,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -875,20 +877,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -897,7 +898,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -934,14 +935,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1006,14 +1007,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1023,7 +1024,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1059,7 +1060,7 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
+ result = storage_update(resultRelationDesc, tupleid, slot,
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
@@ -1151,8 +1152,8 @@ lreplace:;
*
* If it's a HOT update, we mustn't insert new index entries.
*/
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ if ((resultRelInfo->ri_NumIndices > 0) && !storage_tuple_is_heaponly(resultRelationDesc, tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
estate, false, NULL, NIL);
}
@@ -1211,11 +1212,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1226,10 +1228,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1254,7 +1254,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1285,7 +1286,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1313,10 +1316,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1331,7 +1334,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1371,12 +1376,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1569,7 +1576,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..8a6b2172ea 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -331,12 +332,6 @@ TidNext(TidScanState *node)
tidList = node->tss_TidList;
numTids = node->tss_NumTids;
- /*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
/*
* Initialize or advance scan position, depending on direction.
*/
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ //hari
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 083f7d60a7..52779b7256 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -354,7 +355,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -389,7 +390,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024e92..cdd45ef313 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -133,40 +133,19 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -179,7 +158,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 1fe15ede56..799b4edada 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine* heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 9539d67bec..16dfb3e748 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -811,6 +811,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000000..9502c92318
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple *stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+extern void storage_finish_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 95fe02888f..c2e6dc2aef 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -13,32 +13,13 @@
#include "access/htup.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*scan_begin_hook) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index adbcfa1297..203371148c 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 770881849c..8704b7b54c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,7 +196,7 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
@@ -204,8 +204,8 @@ extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c6d3021c85..c19698089b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -2023,7 +2023,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
2.14.1.windows.1
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (176.5K, ../../CAJrrPGdMu8TzxpRZQKK5EL4795by4WAOka1VbxhCtVx7yJoTsQ@mail.gmail.com/10-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 477750defc520950c38dd565ba38562bae6f4789 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:45:04 +1000
Subject: [PATCH 7/8] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1504 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1299 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 2 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 7 +-
src/backend/access/index/indexam.c | 3 +-
src/backend/access/nbtree/nbtinsert.c | 5 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 27 +-
src/backend/catalog/partition.c | 6 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 13 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 16 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 11 +-
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 12 +-
src/backend/executor/nodeIndexonlyscan.c | 4 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 21 +-
src/backend/executor/nodeSeqscan.c | 39 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 30 +-
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 42 +-
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 10 +-
src/include/executor/tqueue.h | 2 +-
src/include/funcapi.h | 2 +-
56 files changed, 1917 insertions(+), 1727 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 5f076efe7c..063e07992b 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index e098202f84..c4b10d6efc 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d20f211a08..b64fec8e4a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,603 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
@@ -1186,317 +443,96 @@ relation_close(Relation relation, LOCKMODE lockmode)
UnlockRelationId(&relid, lockmode);
}
-
-/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
-}
-
+
/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
/* ----------------
@@ -1550,384 +586,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return &(scan->rs_ctup);
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- *
- * Note: if you change the criterion here for what is "dead", fix the
- * planner's get_actual_variable_range() function to match.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
-
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
*
@@ -4762,32 +3420,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index a0e3272f67..12a8f56456 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1666,6 +1666,1094 @@ HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
return true;
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
/*
* heap_fetch - retrieve tuple with given tid
@@ -1818,9 +2906,210 @@ heap_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
/* ----------------------------------------------------------------
* storage AM support routines for heapam
@@ -3793,6 +5082,16 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 8fba61c4f1..a475a859b7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,7 +409,7 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
state->rs_cutoff_multi);
/*
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index d1d7364e7f..76b94dc8c3 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot*
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2543..db5c93b4c7 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,7 +395,7 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
nkeys, key,
true, false);
sysscan->iscan = NULL;
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255369..349a127509 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,7 +606,7 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
scan->xs_cbuf,
scan->xs_snapshot,
&scan->xs_ctup,
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fcdef..0e25e9a363 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,7 +326,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
&all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 0453fd4ac1..975cd5be99 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "bootstrap/bootstrap.h"
#include "catalog/index.h"
@@ -573,18 +574,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -592,7 +593,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -903,25 +904,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..d2a8a06097 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0240df7a08..68c46f720d 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,7 +2280,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -2288,7 +2289,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
true); /* syncscan OK */
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,7 +3005,7 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
@@ -3014,7 +3015,7 @@ validate_index_heapscan(Relation heapRelation,
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 1ab6dba7ae..415421917b 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -988,7 +988,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -997,7 +997,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1013,7 +1013,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc349a..1d048e6394 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471bc83..517e3101cd 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8cd5..145e3c1d65 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71cd6..a51f2e4dfc 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc172..e0f6973a3f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e51ed..6727d154e1 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index beb7f050fe..be20ac7b2d 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2028,10 +2028,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2043,7 +2043,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539035..39850b1b37 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b61aaac284..46bc3da1fb 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1948,8 +1949,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1989,7 +1990,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c9e5ae0832..cd86fb9101 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4540,7 +4540,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4548,7 +4548,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4661,7 +4661,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5064,9 +5064,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5078,7 +5078,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8243,7 +8243,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8251,7 +8251,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8266,7 +8266,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8321,9 +8321,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8352,7 +8352,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10802,8 +10802,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10862,7 +10862,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b6b3..cdfa8ffb3f 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8e2f351949..eb2c4ba081 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..c07f508d5b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2315,8 +2316,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2345,7 +2346,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2711,8 +2712,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2755,7 +2756,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index faa181207a..e24ac9f538 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -447,9 +448,9 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -469,7 +470,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
}
@@ -1121,9 +1122,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1160,7 +1161,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5b88..6ade9df823 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189fa71..5e9daea662 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 8d625b6cbe..6f6861f500 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,20 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +313,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b7a2cbc023..0a5098d1df 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -681,7 +681,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -765,7 +765,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1085,7 +1085,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1093,7 +1093,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 42a4ca94e9..79b74ee2ff 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0ae5873868..d94169cfc8 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3097,7 +3097,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3212,7 +3212,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 60a6cb03d8..7921025178 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
@@ -400,7 +401,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
@@ -685,7 +686,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -764,7 +765,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -865,7 +866,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
estate->es_snapshot,
0,
NULL);
@@ -1023,5 +1024,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 02f6c816aa..abec3a9379 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 8370037c43..a6686ff8a8 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -251,7 +251,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -294,7 +294,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -302,7 +302,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 70f33a9a28..e19cdb557e 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,7 +44,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -53,7 +53,7 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
@@ -399,7 +399,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -617,7 +617,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -689,12 +689,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5351cb8981..f770bc45c5 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,7 +186,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
+ * provided in either StorageTuple or IndexTuple format. Conceivably an
* index AM might fill both fields, in which case we prefer the heap
* format, since it's probably a bit cheaper to fill a slot from.
*/
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 638b17b07c..7330ff983a 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1883..04f85e5f2d 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,9 +29,9 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
+ HeapScanDesc scan); //hari
/* ----------------------------------------------------------------
* Scan Support
@@ -47,7 +47,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -66,7 +66,8 @@ SampleNext(SampleScanState *node)
if (tuple)
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- node->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ //harinode->ss.ss_currentScanDesc->rs_cbuf, /* tuple's buffer */
+ InvalidBuffer,
false); /* don't pfree this pointer */
else
ExecClearTuple(slot);
@@ -244,7 +245,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,7 +350,7 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
scanstate->ss.ps.state->es_snapshot,
0, NULL,
scanstate->use_bulkread,
@@ -358,7 +359,7 @@ tablesample_init(SampleScanState *scanstate)
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
@@ -376,7 +377,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +555,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index d4ac939c9b..839d3a65ec 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,7 +69,7 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
estate->es_snapshot,
0, NULL);
node->ss.ss_currentScanDesc = scandesc;
@@ -78,26 +78,7 @@ SeqNext(SeqScanState *node)
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,12 +251,12 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
+ storage_rescan(scan, /* scan desc */
NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
@@ -322,7 +303,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -353,5 +334,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 80be46029f..d55a752c98 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2091,7 +2091,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2159,7 +2159,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index afe231fca9..418c2a665c 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -627,11 +627,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -655,7 +655,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -686,7 +686,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -854,7 +854,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -962,7 +962,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1683,7 +1683,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1714,8 +1714,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 9a47276274..3d61d96775 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -166,7 +166,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
HeapTupleData htup;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0a9d..fec203dee3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1882,9 +1882,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1911,7 +1911,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2042,13 +2042,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2134,7 +2134,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2142,8 +2142,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2189,7 +2189,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302cf7..74113a75b8 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 44bdcab3b9..61981a0939 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 071b3a9ec9..4924daca76 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 20f1d279e9..03b7cc76d7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/session.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1212,10 +1213,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index cdd45ef313..4cddd73355 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,40 +106,16 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
+
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
@@ -147,8 +123,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -158,8 +132,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 799b4edada..66a96d7101 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 9502c92318..507af714a9 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -19,6 +19,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -36,6 +37,34 @@ typedef enum tuple_data_flags
CTID
} tuple_data_flags;
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot* storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -44,6 +73,15 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple *stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
@@ -72,10 +110,6 @@ extern bool storage_tuple_is_heaponly(Relation relation, StorageTuple tuple);
extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
-extern void storage_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid);
-
extern void storage_sync(Relation rel);
#endif
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d8947a3..7f9bef1374 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e92e..d466c997fb 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,9 +117,9 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index fdc9deb2b2..fe580d552a 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -26,7 +26,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
/* Use these to receive tuples from a shm_mq. */
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2aad3..ab0e0916ea 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,7 +229,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.14.1.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-09-27 16:51 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:32 ` Re: Pluggable storage Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 2 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-09-27 16:51 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hi!
I took a look on this patch. I've following notes for now.
tuple_insert_hook tuple_insert; /* heap_insert */
> tuple_update_hook tuple_update; /* heap_update */
> tuple_delete_hook tuple_delete; /* heap_delete */
I don't think this set of functions provides good enough level of
abstraction for storage AM. This functions encapsulate only low-level work
of insert/update/delete tuples into heap itself. However, it still assumed
that indexes are managed outside of storage AM. I don't think this is
right, assuming that one of most demanded storage API usage would be
different MVCC implementation (for instance, UNDO log based as discussed
upthread). Different MVCC implementation is likely going to manage indexes
in a different way. For example, storage AM utilizing UNDO would implement
in-place update even when indexed columns are modified. Therefore this
piece of code in ExecUpdate() wouldn't be relevant anymore.
/*
> * insert index entries for tuple
> *
> * Note: heap_update returns the tid (location) of the new tuple in
> * the t_self field.
> *
> * If it's a HOT update, we mustn't insert new index entries.
> */
> if ((resultRelInfo->ri_NumIndices > 0) &&
> !storage_tuple_is_heaponly(resultRelationDesc, tuple))
> recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
> estate, false, NULL, NIL);
I'm firmly convinced that this logic should be encapsulated into storage AM
altogether with inserting new index tuples on storage insert. Also, HOT
should be completely encapsulated into heapam. It's quite evident for me
that storage utilizing UNDO wouldn't have anything like our current HOT.
Therefore, I think there shouldn't be hot_search_buffer() API function.
tuple_fetch() may cover hot_search_buffer(). That might require some
signature change of tuple_fetch() (probably, extra arguments).
LockTupleMode and HeapUpdateFailureData shouldn't be private of heapam.
Any fullweight OLTP storage AM should support our tuple lock modes and
should be able to report update failures. HeapUpdateFailureData should be
renamed to something like StorageUpdateFailureData. Contents of
HeapUpdateFailureData seems to me general enough to be supported by any
storage with ItemPointer tuple locator.
storage_setscanlimits() is used only during index build. I think that
since storage AM may have different MVCC implementation then storage AM
should decide how to communicate with indexes including index build.
Therefore, instead of exposing storage_setscanlimits(), the
whole IndexBuildHeapScan() should be encapsulated into storage AM.
Also, BulkInsertState should be private of structure of heapam. Another
storages may have another state for bulk insert. On API level we might
have some abstract pointer instead of BulkInsertState while having
GetBulkInsertState and others as API methods.
storage_freeze_tuple() is called only once from rewrite_heap_tuple(). That
makes me think that tuple_freeze API method is wrong for abstraction. We
probably should make rewrite_heap_tuple() or even the
whole rebuild_relation() an API method...
Heap reloptions are untouched for now. Storage AM should be able to
provide its own specific options just like index AMs do.
That's all I have for now.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-10-09 14:22 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-10-09 14:22 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Sep 27, 2017 at 7:51 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:
> I took a look on this patch. I've following notes for now.
>
> tuple_insert_hook tuple_insert; /* heap_insert */
>> tuple_update_hook tuple_update; /* heap_update */
>> tuple_delete_hook tuple_delete; /* heap_delete */
>
>
> I don't think this set of functions provides good enough level of
> abstraction for storage AM. This functions encapsulate only low-level work
> of insert/update/delete tuples into heap itself. However, it still assumed
> that indexes are managed outside of storage AM. I don't think this is
> right, assuming that one of most demanded storage API usage would be
> different MVCC implementation (for instance, UNDO log based as discussed
> upthread). Different MVCC implementation is likely going to manage indexes
> in a different way. For example, storage AM utilizing UNDO would implement
> in-place update even when indexed columns are modified. Therefore this
> piece of code in ExecUpdate() wouldn't be relevant anymore.
>
> /*
>> * insert index entries for tuple
>> *
>> * Note: heap_update returns the tid (location) of the new tuple in
>> * the t_self field.
>> *
>> * If it's a HOT update, we mustn't insert new index entries.
>> */
>> if ((resultRelInfo->ri_NumIndices > 0) && !storage_tuple_is_heaponly(resultRelationDesc,
>> tuple))
>> recheckIndexes = ExecInsertIndexTuples(slot, &(slot->tts_tid),
>> estate, false, NULL, NIL);
>
>
> I'm firmly convinced that this logic should be encapsulated into storage
> AM altogether with inserting new index tuples on storage insert. Also, HOT
> should be completely encapsulated into heapam. It's quite evident for me
> that storage utilizing UNDO wouldn't have anything like our current HOT.
> Therefore, I think there shouldn't be hot_search_buffer() API function.
> tuple_fetch() may cover hot_search_buffer(). That might require some
> signature change of tuple_fetch() (probably, extra arguments).
>
For me, it's crucial point that pluggable storages should be able to have
different MVCC implementation, and correspondingly have full control over
its interactions with indexes.
Thus, it would be good if we would get consensus on that point. I'd like
other discussion participants to comment whether they agree/disagree and
why.
Any comments?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-10-11 20:08 ` Robert Haas <robertmhaas@gmail.com>
2017-10-11 20:54 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 2 replies; 138+ messages in thread
From: Robert Haas @ 2017-10-11 20:08 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Oct 9, 2017 at 10:22 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> For me, it's crucial point that pluggable storages should be able to have
> different MVCC implementation, and correspondingly have full control over
> its interactions with indexes.
> Thus, it would be good if we would get consensus on that point. I'd like
> other discussion participants to comment whether they agree/disagree and
> why.
> Any comments?
I think it's good for new storage managers to have full control over
interactions with indexes. I'm not sure about the MVCC part. I think
it would be legitimate to want a storage manager to ignore MVCC
altogether - e.g. to build a non-transactional table. I don't know
that it would be a very good idea to have two different full-fledged
MVCC implementations, though. Like Tom says, that would be
replicating a lot of the awfulness of the MySQL model.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-11 20:54 ` Peter Geoghegan <pg@bowt.ie>
1 sibling, 0 replies; 138+ messages in thread
From: Peter Geoghegan @ 2017-10-11 20:54 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Oct 11, 2017 at 1:08 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Mon, Oct 9, 2017 at 10:22 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
>> For me, it's crucial point that pluggable storages should be able to have
>> different MVCC implementation, and correspondingly have full control over
>> its interactions with indexes.
>> Thus, it would be good if we would get consensus on that point. I'd like
>> other discussion participants to comment whether they agree/disagree and
>> why.
>> Any comments?
>
> I think it's good for new storage managers to have full control over
> interactions with indexes. I'm not sure about the MVCC part. I think
> it would be legitimate to want a storage manager to ignore MVCC
> altogether - e.g. to build a non-transactional table.
I agree with Alexander -- if you're going to have a new MVCC
implementation, you have to do significant work within index access
methods. Adding "retail index tuple deletion" is probably just the
beginning. ISTM that you need something like InnoDB's purge thread
when index values change, since two versions of the same index tuple
(each with distinct attribute values) have to physically co-exist for
a time.
> I don't know
> that it would be a very good idea to have two different full-fledged
> MVCC implementations, though. Like Tom says, that would be
> replicating a lot of the awfulness of the MySQL model.
It's not just the MySQL model, FWIW. SQL-on-Hadoop systems like
Impala, certain NoSQL systems, and AFAIK any database system that
claims to have pluggable storage all do it this way. That is, core
transaction management functions (e.g. MVCC snapshot acquisition) is
outsourced to the storage engine. It *is* very cumbersome, but that's
what they do.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-12 20:38 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-10-12 20:38 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Oct 11, 2017 at 11:08 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Mon, Oct 9, 2017 at 10:22 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > For me, it's crucial point that pluggable storages should be able to have
> > different MVCC implementation, and correspondingly have full control over
> > its interactions with indexes.
> > Thus, it would be good if we would get consensus on that point. I'd like
> > other discussion participants to comment whether they agree/disagree and
> > why.
> > Any comments?
>
> I think it's good for new storage managers to have full control over
> interactions with indexes. I'm not sure about the MVCC part. I think
> it would be legitimate to want a storage manager to ignore MVCC
> altogether - e.g. to build a non-transactional table. I don't know
> that it would be a very good idea to have two different full-fledged
> MVCC implementations, though. Like Tom says, that would be
> replicating a lot of the awfulness of the MySQL model.
It's probably that we imply different meaning to "MVCC implementation".
While writing "MVCC implementation" I meant that, for instance, alternative
storage
may implement UNDO chains to store versions of same row. Correspondingly,
it may not have any analogue of our HOT.
However I imply that alternative storage would share our "MVCC model". So,
it
should share our transactional model including transactions,
subtransactions, snapshots etc.
Therefore, if alternative storage is transactional, then in particular it
should be able to fetch tuple with
given TID according to given snapshot. However, how it's implemented
internally is
a black box for us. Thus, we don't insist that tuple should have different
TID after update;
we don't insist there is any analogue of HOT; we don't insist alternative
storage needs vacuum
(or if even it needs vacuum, it might be performed in completely different
way) and so on.
During conversations with you at PGCon and other conferences I had
impression
that you share this view on pluggable storages and MVCC. Probably, we just
express
this view in different words. Or alternatively I might understand you
terribly wrong.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-10-12 21:23 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 17:59 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
0 siblings, 2 replies; 138+ messages in thread
From: Robert Haas @ 2017-10-12 21:23 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Oct 12, 2017 at 4:38 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> It's probably that we imply different meaning to "MVCC implementation".
> While writing "MVCC implementation" I meant that, for instance, alternative
> storage
> may implement UNDO chains to store versions of same row. Correspondingly,
> it may not have any analogue of our HOT.
Yes, the zheap project on which EnterpriseDB is working has precisely
this characteristic.
> However I imply that alternative storage would share our "MVCC model". So,
> it
> should share our transactional model including transactions,
> subtransactions, snapshots etc.
> Therefore, if alternative storage is transactional, then in particular it
> should be able to fetch tuple with
> given TID according to given snapshot. However, how it's implemented
> internally is
> a black box for us. Thus, we don't insist that tuple should have different
> TID after update;
> we don't insist there is any analogue of HOT; we don't insist alternative
> storage needs vacuum
> (or if even it needs vacuum, it might be performed in completely different
> way) and so on.
Fully agreed.
> During conversations with you at PGCon and other conferences I had
> impression
> that you share this view on pluggable storages and MVCC. Probably, we just
> express
> this view in different words. Or alternatively I might understand you
> terribly wrong.
No, it sounds like we are on the same page. I'm only hoping that we
don't end with a bunch of storage engines that each use a different
XID space or something icky like that. I don't think the API should
try to cater to that sort of development.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-13 00:00 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-10-13 00:00 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 8:23 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Oct 12, 2017 at 4:38 PM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > It's probably that we imply different meaning to "MVCC implementation".
> > While writing "MVCC implementation" I meant that, for instance,
> alternative
> > storage
> > may implement UNDO chains to store versions of same row.
> Correspondingly,
> > it may not have any analogue of our HOT.
>
> Yes, the zheap project on which EnterpriseDB is working has precisely
> this characteristic.
>
> > However I imply that alternative storage would share our "MVCC model".
> So,
> > it
> > should share our transactional model including transactions,
> > subtransactions, snapshots etc.
> > Therefore, if alternative storage is transactional, then in particular it
> > should be able to fetch tuple with
> > given TID according to given snapshot. However, how it's implemented
> > internally is
> > a black box for us. Thus, we don't insist that tuple should have
> different
> > TID after update;
> > we don't insist there is any analogue of HOT; we don't insist alternative
> > storage needs vacuum
> > (or if even it needs vacuum, it might be performed in completely
> different
> > way) and so on.
>
> Fully agreed.
Currently I added a snapshot_satisfies API to find out whether the tuple
satisfies the visibility or not with different types of visibility
routines. I feel these
are some how enough to develop a different storage methods like UNDO.
The storage methods can decide internally how to provide the visibility.
+ amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
+ amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
+ amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
+ amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
+ amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
+ amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] =
HeapTupleSatisfiesHistoricMVCC;
+ amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] =
HeapTupleSatisfiesNonVacuumable;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
Currently no changes are carried out in snapshot logic as that is kept
seperate
from storage API.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-10-13 00:55 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-10-13 00:55 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Oct 12, 2017 at 8:00 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
> Currently I added a snapshot_satisfies API to find out whether the tuple
> satisfies the visibility or not with different types of visibility routines.
> I feel these
> are some how enough to develop a different storage methods like UNDO.
> The storage methods can decide internally how to provide the visibility.
>
> + amroutine->snapshot_satisfies[MVCC_VISIBILITY] = HeapTupleSatisfiesMVCC;
> + amroutine->snapshot_satisfies[SELF_VISIBILITY] = HeapTupleSatisfiesSelf;
> + amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
> + amroutine->snapshot_satisfies[TOAST_VISIBILITY] = HeapTupleSatisfiesToast;
> + amroutine->snapshot_satisfies[DIRTY_VISIBILITY] = HeapTupleSatisfiesDirty;
> + amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] =
> HeapTupleSatisfiesHistoricMVCC;
> + amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] =
> HeapTupleSatisfiesNonVacuumable;
> +
> + amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
> + amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
>
> Currently no changes are carried out in snapshot logic as that is kept
> seperate
> from storage API.
That seems like a strange choice of API. I think it should more
integrated with the scan logic. For example, if I'm doing an index
scan, and I get a TID, then I should be able to just say "here's a
TID, give me any tuples associated with that TID that are visible to
the scan snapshot". Then for the current heap it will do
heap_hot_search_buffer, and for zheap it will walk the undo chain and
return the relevant tuple from the chain.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-13 08:28 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 09:25 ` Re: Pluggable storage Kuntal Ghosh <kuntalghosh.2007@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Haribabu Kommi @ 2017-10-13 08:28 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 11:55 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Oct 12, 2017 at 8:00 PM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> > Currently I added a snapshot_satisfies API to find out whether the tuple
> > satisfies the visibility or not with different types of visibility
> routines.
> > I feel these
> > are some how enough to develop a different storage methods like UNDO.
> > The storage methods can decide internally how to provide the visibility.
> >
> > + amroutine->snapshot_satisfies[MVCC_VISIBILITY] =
> HeapTupleSatisfiesMVCC;
> > + amroutine->snapshot_satisfies[SELF_VISIBILITY] =
> HeapTupleSatisfiesSelf;
> > + amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
> > + amroutine->snapshot_satisfies[TOAST_VISIBILITY] =
> HeapTupleSatisfiesToast;
> > + amroutine->snapshot_satisfies[DIRTY_VISIBILITY] =
> HeapTupleSatisfiesDirty;
> > + amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] =
> > HeapTupleSatisfiesHistoricMVCC;
> > + amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] =
> > HeapTupleSatisfiesNonVacuumable;
> > +
> > + amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
> > + amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
> >
> > Currently no changes are carried out in snapshot logic as that is kept
> > seperate
> > from storage API.
>
> That seems like a strange choice of API. I think it should more
> integrated with the scan logic. For example, if I'm doing an index
> scan, and I get a TID, then I should be able to just say "here's a
> TID, give me any tuples associated with that TID that are visible to
> the scan snapshot". Then for the current heap it will do
> heap_hot_search_buffer, and for zheap it will walk the undo chain and
> return the relevant tuple from the chain.
OK, Understood.
I will check along these lines and come up with storage API's.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-10-13 09:25 ` Kuntal Ghosh <kuntalghosh.2007@gmail.com>
2017-10-13 18:37 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Kuntal Ghosh @ 2017-10-13 09:25 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 1:58 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
>
> On Fri, Oct 13, 2017 at 11:55 AM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>> On Thu, Oct 12, 2017 at 8:00 PM, Haribabu Kommi
>> <kommi.haribabu@gmail.com> wrote:
>>
>> That seems like a strange choice of API. I think it should more
>> integrated with the scan logic. For example, if I'm doing an index
>> scan, and I get a TID, then I should be able to just say "here's a
>> TID, give me any tuples associated with that TID that are visible to
>> the scan snapshot". Then for the current heap it will do
>> heap_hot_search_buffer, and for zheap it will walk the undo chain and
>> return the relevant tuple from the chain.
>
>
> OK, Understood.
> I will check along these lines and come up with storage API's.
>
I've some doubts regarding the following function hook:
+typedef bool (*hot_search_buffer_hook) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
As per my understanding, with HOT feature a new tuple placed on the
same page and with all indexed columns the same as its parent row
version does not get new index entries (README.HOT). For some other
storage engine, if we maintain the older version in different storage,
undo for example, and don't require a new index entry, should we still
call it HOT-chain? If not, IMHO, we may have something like
*search_buffer_hook(tid,....,storageTuple,...). Depending on the
underlying storage, one can traverse hot-chain or undo-chain or some
other multi-version strategy for non-key updates.
After a successful index search, most of the index AMs set
(HeapTupleData)xs_ctup->t_self of IndexScanDescData to the tupleid in
the storage. IMHO, some changes are needed here to make it generic.
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
For some storage engine, isn't it possible that the buffer is valid
and the tuple to be stored is formed in memory (for example, tuple
formed from UNDO, in-memory decrypted tuple etc.)
--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 09:25 ` Re: Pluggable storage Kuntal Ghosh <kuntalghosh.2007@gmail.com>
@ 2017-10-13 18:37 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 18:54 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-10-13 18:37 UTC (permalink / raw)
To: Kuntal Ghosh <kuntalghosh.2007@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 5:25 AM, Kuntal Ghosh
<kuntalghosh.2007@gmail.com> wrote:
> For some other
> storage engine, if we maintain the older version in different storage,
> undo for example, and don't require a new index entry, should we still
> call it HOT-chain?
I would say, emphatically, no. HOT is a creature of the existing
heap. If it's creeping into storage APIs they are not really
abstracted from what we have currently.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 09:25 ` Re: Pluggable storage Kuntal Ghosh <kuntalghosh.2007@gmail.com>
2017-10-13 18:37 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-13 18:54 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 0 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-10-13 18:54 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Kuntal Ghosh <kuntalghosh.2007@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 9:37 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Oct 13, 2017 at 5:25 AM, Kuntal Ghosh
> <kuntalghosh.2007@gmail.com> wrote:
> > For some other
> > storage engine, if we maintain the older version in different storage,
> > undo for example, and don't require a new index entry, should we still
> > call it HOT-chain?
>
> I would say, emphatically, no. HOT is a creature of the existing
> heap. If it's creeping into storage APIs they are not really
> abstracted from what we have currently.
+1,
different storage may need to insert entries to only *some* of indexes.
Wherein these new index entries may have either same or new TIDs.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-10-20 03:47 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-10-20 03:47 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 1:58 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
> On Fri, Oct 13, 2017 at 11:55 AM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>> On Thu, Oct 12, 2017 at 8:00 PM, Haribabu Kommi
>> <kommi.haribabu@gmail.com> wrote:
>> > Currently I added a snapshot_satisfies API to find out whether the tuple
>> > satisfies the visibility or not with different types of visibility
>> > routines.
>> > I feel these
>> > are some how enough to develop a different storage methods like UNDO.
>> > The storage methods can decide internally how to provide the visibility.
>> >
>> > + amroutine->snapshot_satisfies[MVCC_VISIBILITY] =
>> > HeapTupleSatisfiesMVCC;
>> > + amroutine->snapshot_satisfies[SELF_VISIBILITY] =
>> > HeapTupleSatisfiesSelf;
>> > + amroutine->snapshot_satisfies[ANY_VISIBILITY] = HeapTupleSatisfiesAny;
>> > + amroutine->snapshot_satisfies[TOAST_VISIBILITY] =
>> > HeapTupleSatisfiesToast;
>> > + amroutine->snapshot_satisfies[DIRTY_VISIBILITY] =
>> > HeapTupleSatisfiesDirty;
>> > + amroutine->snapshot_satisfies[HISTORIC_MVCC_VISIBILITY] =
>> > HeapTupleSatisfiesHistoricMVCC;
>> > + amroutine->snapshot_satisfies[NON_VACUUMABLE_VISIBILTY] =
>> > HeapTupleSatisfiesNonVacuumable;
>> > +
>> > + amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
>> > + amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
>> >
>> > Currently no changes are carried out in snapshot logic as that is kept
>> > seperate
>> > from storage API.
>>
>> That seems like a strange choice of API. I think it should more
>> integrated with the scan logic. For example, if I'm doing an index
>> scan, and I get a TID, then I should be able to just say "here's a
>> TID, give me any tuples associated with that TID that are visible to
>> the scan snapshot". Then for the current heap it will do
>> heap_hot_search_buffer, and for zheap it will walk the undo chain and
>> return the relevant tuple from the chain.
>
>
> OK, Understood.
> I will check along these lines and come up with storage API's.
>
I think what we need here is a way to register satisfies function
(SnapshotSatisfiesFunc) in SnapshotData for different storage engines.
That is the core API to decide visibility with respect to different
storage engines.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-10-25 06:07 ` Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-10-25 06:07 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 20, 2017 at 5:47 AM, Amit Kapila <amit.kapila16@gmail.com> wrote:
> I think what we need here is a way to register satisfies function
> (SnapshotSatisfiesFunc) in SnapshotData for different storage engines.
I don't see how that helps very much. SnapshotSatisfiesFunc takes a
HeapTuple as an argument, and it cares in detail about that tuple's
xmin, xmax, and infomask, and it sets hint bits. All of that is bad,
because an alternative storage engine is likely to use a different
format than HeapTuple and to not have hint bits (or at least not in
the same form we have them now). Also, it doesn't necessarily have a
Boolean answer to the question "can this snapshot see this tuple?".
It may be more like "given this TID, what tuple if any can I see
there?" or "given this tuple, what version of it would I see with this
snapshot?".
Another thing to consider is that, if we could replace satisfiesfunc,
it would probably break some existing code. There are multiple places
in the code that compare snapshot->satisfies to
HeapTupleSatisfiesHistoricMVCC and HeapTupleSatisfiesMVCC.
I think the storage API should just leave snapshots alone. If a
storage engine wants to call HeapTupleSatisfiesVisibility() with that
snapshot, it can do so. Otherwise it can switch on
snapshot->satisfies and handle each case however it likes. I don't
see how generalizing a Snapshot for other storage engines really buys
us anything except complexity and the danger of reducing performance
for the existing heap.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-25 11:59 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Amit Kapila @ 2017-10-25 11:59 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Oct 25, 2017 at 11:37 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Oct 20, 2017 at 5:47 AM, Amit Kapila <amit.kapila16@gmail.com> wrote:
>> I think what we need here is a way to register satisfies function
>> (SnapshotSatisfiesFunc) in SnapshotData for different storage engines.
>
> I don't see how that helps very much. SnapshotSatisfiesFunc takes a
> HeapTuple as an argument, and it cares in detail about that tuple's
> xmin, xmax, and infomask, and it sets hint bits. All of that is bad,
> because an alternative storage engine is likely to use a different
> format than HeapTuple and to not have hint bits (or at least not in
> the same form we have them now). Also, it doesn't necessarily have a
> Boolean answer to the question "can this snapshot see this tuple?".
> It may be more like "given this TID, what tuple if any can I see
> there?" or "given this tuple, what version of it would I see with this
> snapshot?".
>
> Another thing to consider is that, if we could replace satisfiesfunc,
> it would probably break some existing code. There are multiple places
> in the code that compare snapshot->satisfies to
> HeapTupleSatisfiesHistoricMVCC and HeapTupleSatisfiesMVCC.
>
> I think the storage API should just leave snapshots alone. If a
> storage engine wants to call HeapTupleSatisfiesVisibility() with that
> snapshot, it can do so. Otherwise it can switch on
> snapshot->satisfies and handle each case however it likes.
>
How will it switch satisfies at runtime? There are places where we
might know which visibility function (*MVCC , *Dirty, etc) needs to be
called, but I think there are other places (like heap_fetch) where it
is not clear and we decide based on what is stored in
snapshot->satisfies.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2017-10-27 05:06 ` Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 10:42 ` Re: [HACKERS] Pluggable storage Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 2 replies; 138+ messages in thread
From: Robert Haas @ 2017-10-27 05:06 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Oct 25, 2017 at 1:59 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:
>> Another thing to consider is that, if we could replace satisfiesfunc,
>> it would probably break some existing code. There are multiple places
>> in the code that compare snapshot->satisfies to
>> HeapTupleSatisfiesHistoricMVCC and HeapTupleSatisfiesMVCC.
>>
>> I think the storage API should just leave snapshots alone. If a
>> storage engine wants to call HeapTupleSatisfiesVisibility() with that
>> snapshot, it can do so. Otherwise it can switch on
>> snapshot->satisfies and handle each case however it likes.
>>
>
> How will it switch satisfies at runtime? There are places where we
> might know which visibility function (*MVCC , *Dirty, etc) needs to be
> called, but I think there are other places (like heap_fetch) where it
> is not clear and we decide based on what is stored in
> snapshot->satisfies.
An alternative storage engine needs to provide its own implementation
of heap_fetch, and that replacement implementation can implement MVCC
and other snapshot behavior in any way it likes.
My point here is that I think it's better if the table access method
stuff doesn't end up modifying snapshots. I think it's fine for a
table access method to get passed a standard snapshot. Some code may
be needed to cater to the access method's specific needs, but that
code can live inside the table access method, without contaminating
the snapshot stuff. We have to try to draw some boundary around table
access methods -- we don't want to end up teaching everything in the
system about them.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-31 09:59 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-10-31 09:59 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 27, 2017 at 4:06 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Wed, Oct 25, 2017 at 1:59 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
> >> Another thing to consider is that, if we could replace satisfiesfunc,
> >> it would probably break some existing code. There are multiple places
> >> in the code that compare snapshot->satisfies to
> >> HeapTupleSatisfiesHistoricMVCC and HeapTupleSatisfiesMVCC.
> >>
> >> I think the storage API should just leave snapshots alone. If a
> >> storage engine wants to call HeapTupleSatisfiesVisibility() with that
> >> snapshot, it can do so. Otherwise it can switch on
> >> snapshot->satisfies and handle each case however it likes.
> >>
> >
> > How will it switch satisfies at runtime? There are places where we
> > might know which visibility function (*MVCC , *Dirty, etc) needs to be
> > called, but I think there are other places (like heap_fetch) where it
> > is not clear and we decide based on what is stored in
> > snapshot->satisfies.
>
> An alternative storage engine needs to provide its own implementation
> of heap_fetch, and that replacement implementation can implement MVCC
> and other snapshot behavior in any way it likes.
In the current set of patches, I changed the snapshot->satisfies function
point to an enum type, Based on the snapshot visibility type, internally
the storage AM will call the corresponding visibility function.
Additional changes that are done in the patches compared to earlier
patches apart from rebase.
0004-Adding tuple visibility:
Tuple visibility API functions are reduced to 3. It still needs further
optimization.
Tuple satisfies visibility check is added to heap function that don't have
currently.
0006-tuple-insert
Move the index tuple insertion logic inside storage Am with a function
pointer, applicable to insert and updates. yet to handle the insert index
tuples
for multi insert scenario.
Removed the speculative finish API. Yet to remove abort API.
Known pending items:
1. Provide a generic new API like heap_fetch to remove heap_hot_search
and visibility functions usage.
2. Move toast table details into storage AM, Toast method depends on
storage.
and also toast flattening function needs to be replaced with some generic
functions.
3. Provide new API to get the heaptuple from index or building the index.
may be the same API like heap_fetch may satisfy this requirement also.
4. Bulk insert functionality needs a separate API to deal with all storage
AMs.
5. Provide a framework to add reloptions based on storage.
6. Needs a generic API to support rewrite the heap, (cluster command)
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (181.3K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/3-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 46f3c93cf2acb4a0943644cb5db76577f140db22 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Tue, 31 Oct 2017 17:49:18 +1100
Subject: [PATCH 7/8] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1511 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1305 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 4 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 11 +-
src/backend/access/index/indexam.c | 13 +-
src/backend/access/nbtree/nbtinsert.c | 7 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 59 +-
src/backend/catalog/partition.c | 24 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 17 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 15 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 19 +-
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 14 +-
src/backend/executor/nodeIndexonlyscan.c | 8 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 34 +-
src/backend/executor/nodeSeqscan.c | 45 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/postmaster/postmaster.c | 4 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 30 +-
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 39 +
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 12 +-
src/include/executor/tqueue.h | 4 +-
src/include/funcapi.h | 2 +-
57 files changed, 1982 insertions(+), 1784 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 830e74fd07..bc8b423975 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index e098202f84..c4b10d6efc 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 327ad30b2d..c785268921 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,610 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
- {
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
* This is formatted so oddly so that the correspondence to the macro
@@ -1189,320 +439,99 @@ relation_close(Relation relation, LOCKMODE lockmode)
RelationClose(relation);
if (lockmode != NoLock)
- UnlockRelationId(&relid, lockmode);
-}
-
-
-/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
+ UnlockRelationId(&relid, lockmode);
}
+
/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
/* ----------------
@@ -1556,382 +585,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return heap_copytuple(&(scan->rs_ctup));
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !HeapTupleUpdateXmaxMatchesXmin(prev_xmax, heapTuple->t_data))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- *
- * Note: if you change the criterion here for what is "dead", fix the
- * planner's get_actual_variable_range() function to match.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
-
/*
* HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
*
@@ -4835,32 +3488,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index d6352df472..6e1817b1fd 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1700,6 +1700,1100 @@ HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
}
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
+ {
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
/*
* heap_fetch - retrieve tuple with given tid
@@ -1852,9 +2946,210 @@ heap_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
/* ----------------------------------------------------------------
* storage AM support routines for heapam
@@ -3876,6 +5171,16 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 84ac4bfade..4691e44ecc 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,8 +409,8 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
- state->rs_cutoff_multi);
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
+ state->rs_cutoff_multi);
/*
* Invalid ctid means that ctid should point to the tuple itself. We'll
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 870490d00f..0b83ecf1bc 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot *
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2543..26a9ccb657 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,9 +395,9 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
- nkeys, key,
- true, false);
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
+ nkeys, key,
+ true, false);
sysscan->iscan = NULL;
}
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255369..407e381a91 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,12 +606,12 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
- scan->xs_cbuf,
- scan->xs_snapshot,
- &scan->xs_ctup,
- &all_dead,
- !scan->xs_continue_hot);
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
+ scan->xs_cbuf,
+ scan->xs_snapshot,
+ &scan->xs_ctup,
+ &all_dead,
+ !scan->xs_continue_hot);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
if (got_heap_tuple)
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index bf963fcdef..d3b67abf4a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,8 +326,8 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
- &all_dead))
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
+ &all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..a73a363a49 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "bootstrap/bootstrap.h"
@@ -586,18 +587,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -605,7 +606,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -916,25 +917,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..d2a8a06097 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 17be9a4042..a201b00cae 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,16 +2280,16 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- allow_sync); /* syncscan OK? */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ allow_sync); /* syncscan OK? */
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- true); /* syncscan OK */
-
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ true); /* syncscan OK */
+
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,17 +3005,17 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- false); /* syncscan not OK */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ false); /* syncscan not OK */
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 5daa8a1c19..a5a5268d12 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -709,11 +710,11 @@ extern PartitionBoundInfo
partition_bounds_copy(PartitionBoundInfo src,
PartitionKey key)
{
- PartitionBoundInfo dest;
- int i;
- int ndatums;
- int partnatts;
- int num_indexes;
+ PartitionBoundInfo dest;
+ int i;
+ int ndatums;
+ int partnatts;
+ int num_indexes;
dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
@@ -732,11 +733,11 @@ partition_bounds_copy(PartitionBoundInfo src,
if (src->kind != NULL)
{
dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
- sizeof(PartitionRangeDatumKind *));
+ sizeof(PartitionRangeDatumKind *));
for (i = 0; i < ndatums; i++)
{
dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
- sizeof(PartitionRangeDatumKind));
+ sizeof(PartitionRangeDatumKind));
memcpy(dest->kind[i], src->kind[i],
sizeof(PartitionRangeDatumKind) * key->partnatts);
@@ -747,7 +748,8 @@ partition_bounds_copy(PartitionBoundInfo src,
for (i = 0; i < ndatums; i++)
{
- int j;
+ int j;
+
dest->datums[i] = (Datum *) palloc(sizeof(Datum) * partnatts);
for (j = 0; j < partnatts; j++)
@@ -1074,7 +1076,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -1083,7 +1085,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1099,7 +1101,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc349a..1d048e6394 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471bc83..517e3101cd 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8cd5..145e3c1d65 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71cd6..a51f2e4dfc 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc172..e0f6973a3f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e51ed..6727d154e1 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 69af210dba..79f2085fb3 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2036,10 +2036,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2051,7 +2051,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539035..39850b1b37 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3f615b6260..5209083a72 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1947,8 +1948,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1988,7 +1989,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e1ca60b35b..3f928a8051 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4540,7 +4540,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4548,7 +4548,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4662,7 +4662,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5065,9 +5065,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5079,7 +5079,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8246,7 +8246,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8254,7 +8254,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8269,7 +8269,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8324,9 +8324,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8355,7 +8355,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10805,8 +10805,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10865,7 +10865,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b6b3..cdfa8ffb3f 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 680a65cad5..a0f3db5bdb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7df942b18b..b95e3fc5ab 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2387,8 +2388,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2417,7 +2418,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2783,8 +2784,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2827,7 +2828,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index cbd6e9b161..bafc15eac6 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -68,7 +69,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
-static List *expand_vacuum_rel(VacuumRelation *vrel);
+static List *expand_vacuum_rel(VacuumRelation * vrel);
static List *get_all_vacuum_rels(void);
static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
@@ -423,7 +424,7 @@ vacuum(int options, List *relations, VacuumParams *params,
* are made in vac_context.
*/
static List *
-expand_vacuum_rel(VacuumRelation *vrel)
+expand_vacuum_rel(VacuumRelation * vrel)
{
List *vacrels = NIL;
MemoryContext oldcontext;
@@ -533,9 +534,9 @@ get_all_vacuum_rels(void)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
MemoryContext oldcontext;
@@ -562,7 +563,7 @@ get_all_vacuum_rels(void)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
return vacrels;
@@ -1213,9 +1214,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1252,7 +1253,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5b88..dde843174e 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index ab533cf9c7..e852718100 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 24b5ff7298..ed2c0ab80c 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,19 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +312,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index d618b3d903..742f73176c 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -683,7 +683,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -767,7 +767,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1087,7 +1087,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1095,7 +1095,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 98eb777421..8ce51e5bd5 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 2b118359b5..d8c4119972 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3119,7 +3119,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3244,7 +3244,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index c34898f241..9e24b1abd8 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapHeapscan.h"
@@ -400,8 +401,8 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
- &heapTuple, NULL, true))
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ &heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
@@ -685,7 +686,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
if (node->tbmiterator)
tbm_end_iterate(node->tbmiterator);
@@ -764,7 +765,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -865,10 +866,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
- estate->es_snapshot,
- 0,
- NULL);
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
+ estate->es_snapshot,
+ 0,
+ NULL);
/*
* get the scan type from the relation descriptor.
@@ -1024,5 +1025,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 02f6c816aa..d3de6c5824 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 639f4f5af8..edb8046180 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -251,7 +251,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -294,7 +294,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -302,7 +302,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 5625b12521..3381813455 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,7 +44,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -53,8 +53,8 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
- bool nowait, bool *done);
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+ bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
static void gather_merge_init(GatherMergeState *gm_state);
@@ -399,7 +399,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -617,7 +617,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -689,12 +689,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 9368ca04f8..07346435cd 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,9 +186,9 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
- * index AM might fill both fields, in which case we prefer the heap
- * format, since it's probably a bit cheaper to fill a slot from.
+ * provided in either StorageTuple or IndexTuple format. Conceivably
+ * an index AM might fill both fields, in which case we prefer the
+ * heap format, since it's probably a bit cheaper to fill a slot from.
*/
if (scandesc->xs_hitup)
{
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 262008240d..2726933d9d 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1883..64f3dd8be0 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,10 +29,12 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
HeapScanDesc scan);
+//hari
+
/* ----------------------------------------------------------------
* Scan Support
* ----------------------------------------------------------------
@@ -47,7 +49,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -244,7 +246,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,19 +351,19 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
- scanstate->ss.ps.state->es_snapshot,
- 0, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ scanstate->ss.ps.state->es_snapshot,
+ 0, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
pfree(params);
@@ -376,7 +378,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +556,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 76bec780a8..0521df1fa9 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,35 +69,16 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
- estate->es_snapshot,
- 0, NULL);
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
+ estate->es_snapshot,
+ 0, NULL);
node->ss.ss_currentScanDesc = scandesc;
}
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,13 +251,13 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
- NULL); /* new scan keys */
+ storage_rescan(scan, /* scan desc */
+ NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
}
@@ -323,7 +304,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -354,5 +335,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 02868749f6..a68584378b 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2092,7 +2092,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2175,7 +2175,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 40292b86c1..557cb2175c 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -633,11 +633,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -661,7 +661,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -692,7 +692,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -860,7 +860,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -968,7 +968,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1689,7 +1689,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1720,8 +1720,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 9a47276274..3d61d96775 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -166,7 +166,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
HeapTupleData htup;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb01b..0d2e1733bf 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1883,9 +1883,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1912,7 +1912,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2043,13 +2043,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2135,7 +2135,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2143,8 +2143,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2190,7 +2190,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 3a0b49c7c4..36808c4067 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 2b2b993e2c..88086f3d48 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4268,8 +4268,8 @@ BackendInitialize(Port *port)
*
* postgres: walsender <user> <host> <activity>
*
- * To achieve that, we pass "walsender" as username and username
- * as dbname to init_ps_display(). XXX: should add a new variant of
+ * To achieve that, we pass "walsender" as username and username as dbname
+ * to init_ps_display(). XXX: should add a new variant of
* init_ps_display() to avoid abusing the parameters like this.
*/
if (am_walsender)
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index a613ef4757..83ec2dfcbe 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 071b3a9ec9..4924daca76 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 20f1d279e9..03b7cc76d7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/session.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1212,10 +1213,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index b1cb80c251..db87b686c5 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,40 +106,16 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
+
extern void setLastTid(const ItemPointer tid);
extern bool HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax,
@@ -150,8 +126,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -161,8 +135,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 14aad60546..597f2bd2f4 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 447935ba10..ec5dc89a26 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -20,6 +20,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -42,6 +43,34 @@ typedef enum tuple_data_flags
typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
bool *specConflict, List *arbiterIndexes);
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -50,6 +79,16 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d8947a3..7f9bef1374 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e92e..2781975530 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,10 +117,10 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
- int *attnum, Datum *Values, const char *Nulls);
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+ int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index fdc9deb2b2..9905ecd8ba 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -26,7 +26,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
/* Use these to receive tuples from a shm_mq. */
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
- bool nowait, bool *done);
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
+ bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 223eef28d1..54b37f7db5 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -237,7 +237,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.14.2.windows.1
[application/octet-stream] 0008-Remove-HeapScanDesc-usage-outside-heap.patch (92.4K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/4-0008-Remove-HeapScanDesc-usage-outside-heap.patch)
download | inline diff:
From 6503363a3bda844c26abac48c145722dfc493c90 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Tue, 31 Oct 2017 17:49:50 +1100
Subject: [PATCH 8/8] Remove HeapScanDesc usage outside heap
HeapScanDesc is divided into two scan descriptors.
StorageScanDesc and HeapPageScanDesc.
StorageScanDesc has common members that are should
be available across all the storage routines and
HeapPageScanDesc is avaiable only for the storage
routine that supports Heap storage with page format.
The HeapPageScanDesc is used internally by the heapam
storage routine and also this is exposed to Bitmap Heap
and Sample scan's as they depend on the Heap page format.
while generating the Bitmap Heap and Sample scan's,
the planner now checks whether the storage routine
supports returning HeapPageScanDesc or not? Based on
this decision, the planner plans above two plans.
---
contrib/pgrowlocks/pgrowlocks.c | 4 +-
contrib/pgstattuple/pgstattuple.c | 10 +-
contrib/tsm_system_rows/tsm_system_rows.c | 18 +-
contrib/tsm_system_time/tsm_system_time.c | 8 +-
src/backend/access/heap/heapam.c | 37 +--
src/backend/access/heap/heapam_storage.c | 424 +++++++++++++++++------------
src/backend/access/heap/storageam.c | 51 +++-
src/backend/access/index/genam.c | 4 +-
src/backend/access/tablesample/system.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 4 +-
src/backend/catalog/index.c | 8 +-
src/backend/catalog/partition.c | 2 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/cluster.c | 4 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 10 +-
src/backend/commands/tablespace.c | 10 +-
src/backend/commands/typecmds.c | 4 +-
src/backend/commands/vacuum.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 67 ++---
src/backend/executor/nodeSamplescan.c | 50 ++--
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/optimizer/util/plancat.c | 10 +-
src/backend/postmaster/autovacuum.c | 4 +-
src/backend/postmaster/pgstat.c | 2 +-
src/backend/replication/logical/launcher.c | 2 +-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/utils/init/postinit.c | 2 +-
src/include/access/heapam.h | 4 +-
src/include/access/relscan.h | 47 ++--
src/include/access/storageam.h | 44 +--
src/include/access/storageamapi.h | 42 +--
src/include/nodes/execnodes.h | 4 +-
39 files changed, 512 insertions(+), 399 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index bc8b423975..762d969ecd 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -56,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -71,7 +71,7 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index c4b10d6efc..32ac121e92 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -328,7 +329,8 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
- nblocks = scan->rs_nblocks; /* # blocks to be scanned */
+ pagescan = storageam_get_heappagescandesc(scan);
+ nblocks = pagescan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
@@ -364,7 +366,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -377,7 +379,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458ec91..14120291d0 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -221,14 +221,14 @@ system_rows_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -258,7 +258,7 @@ system_rows_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -291,7 +291,7 @@ system_rows_nextsampletuple(SampleScanState *node,
*/
/* We rely on the data accumulated in pagemode access */
- Assert(scan->rs_pageatatime);
+ Assert(pagescan->rs_pageatatime);
for (;;)
{
/* Advance to next possible offset on page */
@@ -308,7 +308,7 @@ system_rows_nextsampletuple(SampleScanState *node,
}
/* Found a candidate? */
- if (SampleOffsetVisible(tupoffset, scan))
+ if (SampleOffsetVisible(tupoffset, pagescan))
{
sampler->donetuples++;
break;
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025414..aa7252215a 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
instr_time cur_time;
/* First call within scan? */
@@ -232,14 +232,14 @@ system_time_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -275,7 +275,7 @@ system_time_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index c785268921..c8e9477da2 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -107,8 +107,9 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_mo
* which tuples on the page are visible.
*/
void
-heapgetpage(HeapScanDesc scan, BlockNumber page)
+heapgetpage(StorageScanDesc sscan, BlockNumber page)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
Buffer buffer;
Snapshot snapshot;
Page dp;
@@ -118,13 +119,13 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
ItemId lpp;
bool all_visible;
- Assert(page < scan->rs_nblocks);
+ Assert(page < scan->rs_pagescan.rs_nblocks);
/* release previous scan buffer, if any */
- if (BufferIsValid(scan->rs_cbuf))
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
{
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
}
/*
@@ -135,20 +136,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
CHECK_FOR_INTERRUPTS();
/* read page using selected strategy */
- scan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM, page,
- RBM_NORMAL, scan->rs_strategy);
- scan->rs_cblock = page;
+ scan->rs_scan.rs_cbuf = ReadBufferExtended(scan->rs_scan.rs_rd, MAIN_FORKNUM, page,
+ RBM_NORMAL, scan->rs_pagescan.rs_strategy);
+ scan->rs_scan.rs_cblock = page;
- if (!scan->rs_pageatatime)
+ if (!scan->rs_pagescan.rs_pageatatime)
return;
- buffer = scan->rs_cbuf;
- snapshot = scan->rs_snapshot;
+ buffer = scan->rs_scan.rs_cbuf;
+ snapshot = scan->rs_scan.rs_snapshot;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
- heap_page_prune_opt(scan->rs_rd, buffer);
+ heap_page_prune_opt(scan->rs_scan.rs_rd, buffer);
/*
* We must hold share lock on the buffer content while examining tuple
@@ -158,7 +159,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
ntup = 0;
@@ -193,7 +194,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
HeapTupleData loctup;
bool valid;
- loctup.t_tableOid = RelationGetRelid(scan->rs_rd);
+ loctup.t_tableOid = RelationGetRelid(scan->rs_scan.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
@@ -201,20 +202,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, &loctup,
buffer, snapshot);
if (valid)
- scan->rs_vistuples[ntup++] = lineoff;
+ scan->rs_pagescan.rs_vistuples[ntup++] = lineoff;
}
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ scan->rs_pagescan.rs_ntuples = ntup;
}
#if defined(DISABLE_COMPLEX_MACRO)
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 6e1817b1fd..bf9f3f39d9 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1745,7 +1745,7 @@ retry:
else
{
SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ sync_startpage = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
goto retry;
}
}
@@ -1787,10 +1787,10 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* starting block number, modulo nblocks.
*/
nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
+ if (nallocated >= scan->rs_pagescan.rs_nblocks)
page = InvalidBlockNumber; /* all blocks have been allocated */
else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_pagescan.rs_nblocks;
/*
* Report scan location. Normally, we report the current page number.
@@ -1799,12 +1799,12 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* doesn't slew backwards. We only report the position at the end of the
* scan once, though: subsequent callers will report nothing.
*/
- if (scan->rs_syncscan)
+ if (scan->rs_pagescan.rs_syncscan)
{
if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ ss_report_location(scan->rs_scan.rs_rd, page);
+ else if (nallocated == scan->rs_pagescan.rs_nblocks)
+ ss_report_location(scan->rs_scan.rs_rd, parallel_scan->phs_startblock);
}
return page;
@@ -1833,9 +1833,9 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* lock that ensures the interesting tuple(s) won't change.)
*/
if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ scan->rs_pagescan.rs_nblocks = scan->rs_parallel->phs_nblocks;
else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+ scan->rs_pagescan.rs_nblocks = RelationGetNumberOfBlocks(scan->rs_scan.rs_rd);
/*
* If the table is large relative to NBuffers, use a bulk-read access
@@ -1849,8 +1849,8 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* Note that heap_parallelscan_initialize has a very similar test; if you
* change this, consider changing that one, too.
*/
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
+ if (!RelationUsesLocalBuffers(scan->rs_scan.rs_rd) &&
+ scan->rs_pagescan.rs_nblocks > NBuffers / 4)
{
allow_strat = scan->rs_allow_strat;
allow_sync = scan->rs_allow_sync;
@@ -1861,20 +1861,20 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (allow_strat)
{
/* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ if (scan->rs_pagescan.rs_strategy == NULL)
+ scan->rs_pagescan.rs_strategy = GetAccessStrategy(BAS_BULKREAD);
}
else
{
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
+ scan->rs_pagescan.rs_strategy = NULL;
}
if (scan->rs_parallel != NULL)
{
/* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ scan->rs_pagescan.rs_syncscan = scan->rs_parallel->phs_syncscan;
}
else if (keep_startblock)
{
@@ -1883,25 +1883,25 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ scan->rs_pagescan.rs_syncscan = (allow_sync && synchronize_seqscans);
}
else if (allow_sync && synchronize_seqscans)
{
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ scan->rs_pagescan.rs_syncscan = true;
+ scan->rs_pagescan.rs_startblock = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
}
else
{
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
+ scan->rs_pagescan.rs_syncscan = false;
+ scan->rs_pagescan.rs_startblock = 0;
}
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
+ scan->rs_pagescan.rs_numblocks = InvalidBlockNumber;
+ scan->rs_scan.rs_inited = false;
scan->rs_ctup.t_data = NULL;
ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
/* page-at-a-time fields are always invalid when not rs_inited */
@@ -1909,7 +1909,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* copy the scan key, if appropriate
*/
if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+ memcpy(scan->rs_scan.rs_key, key, scan->rs_scan.rs_nkeys * sizeof(ScanKeyData));
/*
* Currently, we don't have a stats counter for bitmap heap scans (but the
@@ -1917,7 +1917,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* update stats for tuple fetches there)
*/
if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
+ pgstat_count_heap_scan(scan->rs_scan.rs_rd);
}
@@ -1951,7 +1951,7 @@ heapgettup(HeapScanDesc scan,
ScanKey key)
{
HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
+ Snapshot snapshot = scan->rs_scan.rs_snapshot;
bool backward = ScanDirectionIsBackward(dir);
BlockNumber page;
bool finished;
@@ -1966,14 +1966,14 @@ heapgettup(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -1986,29 +1986,29 @@ heapgettup(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc) scan, page);
lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
/* page and lineoff now reference the physically next tid */
@@ -2019,14 +2019,14 @@ heapgettup(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2037,30 +2037,30 @@ heapgettup(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc) scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineoff = lines; /* final offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
@@ -2076,20 +2076,20 @@ heapgettup(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2120,21 +2120,21 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
tuple,
snapshot,
- scan->rs_cbuf);
+ scan->rs_scan.rs_cbuf);
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, tuple,
+ scan->rs_scan.rs_cbuf, snapshot);
if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -2159,17 +2159,17 @@ heapgettup(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* advance to next/prior page and detect end of scan
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2180,10 +2180,10 @@ heapgettup(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2197,8 +2197,8 @@ heapgettup(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2206,21 +2206,21 @@ heapgettup(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc) scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber((Page) dp);
linesleft = lines;
if (backward)
@@ -2271,14 +2271,14 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2291,27 +2291,27 @@ heapgettup_pagemode(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc) scan, page);
lineindex = 0;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
+ page = scan->rs_scan.rs_cblock; /* current page */
+ lineindex = scan->rs_pagescan.rs_cindex + 1;
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
/* page and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
@@ -2321,14 +2321,14 @@ heapgettup_pagemode(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2339,32 +2339,32 @@ heapgettup_pagemode(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc) scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineindex = lines - 1;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
- lineindex = scan->rs_cindex - 1;
+ lineindex = scan->rs_pagescan.rs_cindex - 1;
}
/* page and lineindex now reference the previous visible tid */
@@ -2375,20 +2375,20 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2397,8 +2397,8 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_len = ItemIdGetLength(lpp);
/* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+ Assert(scan->rs_pagescan.rs_cindex < scan->rs_pagescan.rs_ntuples);
+ Assert(lineoff == scan->rs_pagescan.rs_vistuples[scan->rs_pagescan.rs_cindex]);
return;
}
@@ -2411,7 +2411,7 @@ heapgettup_pagemode(HeapScanDesc scan,
{
while (linesleft > 0)
{
- lineoff = scan->rs_vistuples[lineindex];
+ lineoff = scan->rs_pagescan.rs_vistuples[lineindex];
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2422,7 +2422,9 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
+ tuple, scan->rs_scan.rs_snapshot,
+ scan->rs_scan.rs_cbuf))
{
/*
* if current tuple qualifies, return it.
@@ -2431,17 +2433,17 @@ heapgettup_pagemode(HeapScanDesc scan,
{
bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
}
else
{
- scan->rs_cindex = lineindex;
+ scan->rs_pagescan.rs_cindex = lineindex;
return;
}
}
@@ -2462,10 +2464,10 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2476,10 +2478,10 @@ heapgettup_pagemode(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2493,8 +2495,8 @@ heapgettup_pagemode(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2502,20 +2504,20 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc) scan, page);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
linesleft = lines;
if (backward)
lineindex = lines - 1;
@@ -2525,7 +2527,7 @@ heapgettup_pagemode(HeapScanDesc scan,
}
-static HeapScanDesc
+static StorageScanDesc
heapam_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -2552,12 +2554,12 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
*/
scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
+ scan->rs_scan.rs_rd = relation;
+ scan->rs_scan.rs_snapshot = snapshot;
+ scan->rs_scan.rs_nkeys = nkeys;
scan->rs_bitmapscan = is_bitmapscan;
scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_pagescan.rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_temp_snap = temp_snap;
@@ -2566,7 +2568,7 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
*/
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
/*
* For a seqscan in a serializable transaction, acquire a predicate lock
@@ -2590,13 +2592,29 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* initscan() and we don't want to allocate memory again
*/
if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ scan->rs_scan.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
- scan->rs_key = NULL;
+ scan->rs_scan.rs_key = NULL;
initscan(scan, key, false);
- return scan;
+ return (StorageScanDesc) scan;
+}
+
+static ParallelHeapScanDesc
+heapam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return scan->rs_parallel;
+}
+
+static HeapPageScanDesc
+heapam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return &scan->rs_pagescan;
}
/* ----------------
@@ -2604,21 +2622,23 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* ----------------
*/
static void
-heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+heapam_rescan(StorageScanDesc sscan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
if (set_params)
{
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_scan.rs_snapshot);
}
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* reinitialize scan descriptor
@@ -2649,29 +2669,31 @@ heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
* ----------------
*/
static void
-heapam_endscan(HeapScanDesc scan)
+heapam_endscan(StorageScanDesc sscan)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* decrement relation reference count and free scan descriptor storage
*/
- RelationDecrementReferenceCount(scan->rs_rd);
+ RelationDecrementReferenceCount(scan->rs_scan.rs_rd);
- if (scan->rs_key)
- pfree(scan->rs_key);
+ if (scan->rs_scan.rs_key)
+ pfree(scan->rs_scan.rs_key);
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ UnregisterSnapshot(scan->rs_scan.rs_snapshot);
pfree(scan);
}
@@ -2683,12 +2705,14 @@ heapam_endscan(HeapScanDesc scan)
* ----------------
*/
static void
-heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+heapam_scan_update_snapshot(StorageScanDesc sscan, Snapshot snapshot)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(IsMVCCSnapshot(snapshot));
RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
+ scan->rs_scan.rs_snapshot = snapshot;
scan->rs_temp_snap = true;
}
@@ -2717,7 +2741,7 @@ heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
static StorageTuple
-heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+heapam_getnext(StorageScanDesc sscan, ScanDirection direction)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2725,11 +2749,11 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
HEAPAMDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2743,7 +2767,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
*/
HEAPAMDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return &(scan->rs_ctup);
}
@@ -2763,7 +2787,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
#endif
static TupleTableSlot *
-heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+heapam_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2771,11 +2795,11 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2790,11 +2814,34 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
*/
HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
slot, InvalidBuffer, true);
}
+static StorageTuple
+heapam_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+ Page dp;
+ ItemId lp;
+
+ dp = (Page) BufferGetPage(scan->rs_scan.rs_cbuf);
+ lp = PageGetItemId(dp, offset);
+ Assert(ItemIdIsNormal(lp));
+
+ scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+ scan->rs_ctup.t_len = ItemIdGetLength(lp);
+ scan->rs_ctup.t_tableOid = scan->rs_scan.rs_rd->rd_id;
+ ItemPointerSet(&scan->rs_ctup.t_self, blkno, offset);
+
+ pgstat_count_heap_fetch(scan->rs_scan.rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+
+
/*
* heap_fetch - retrieve tuple with given tid
*
@@ -3109,18 +3156,19 @@ heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
static void
-heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+heapam_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+ Assert(!scan->rs_scan.rs_inited); /* else too late to change */
+ Assert(!scan->rs_pagescan.rs_syncscan); /* else rs_startblock is
+ * significant */
/* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+ Assert(startBlk == 0 || startBlk < scan->rs_pagescan.rs_nblocks);
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
+ scan->rs_pagescan.rs_startblock = startBlk;
+ scan->rs_pagescan.rs_numblocks = numBlks;
}
@@ -5175,12 +5223,26 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->scansetlimits = heapam_setscanlimits;
amroutine->scan_getnext = heapam_getnext;
amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_fetch_tuple_from_offset = heapam_fetch_tuple_from_offset;
amroutine->scan_end = heapam_endscan;
amroutine->scan_rescan = heapam_rescan;
amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
amroutine->tuple_freeze = heapam_freeze_tuple;
amroutine->hot_search_buffer = heapam_hot_search_buffer;
+ /*
+ * The following routine needs to be provided when the storage support
+ * parallel sequential scan
+ */
+ amroutine->scan_get_parallelheapscandesc = heapam_get_parallelheapscandesc;
+
+ /*
+ * The following routine needs to be provided when the storage support
+ * BitmapHeap and Sample Scans
+ */
+ amroutine->scan_get_heappagescandesc = heapam_get_heappagescandesc;
+
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 0b83ecf1bc..9d6e9e9ea1 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -54,7 +54,7 @@
* Caller must hold a suitable lock on the correct relation.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
{
Snapshot snapshot;
@@ -67,6 +67,25 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
true, true, true, false, false, true);
}
+ParallelHeapScanDesc
+storageam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_get_parallelheapscandesc(sscan);
+}
+
+HeapPageScanDesc
+storageam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ /*
+ * Planner should have already validated whether the current storage
+ * supports Page scans are not? This function will be called only from
+ * Bitmap Heap scan and sample scan
+ */
+ Assert(sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc != NULL);
+
+ return sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc(sscan);
+}
+
/*
* heap_setscanlimits - restrict range of a heapscan
*
@@ -74,7 +93,7 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
}
@@ -104,7 +123,7 @@ storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numB
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -112,7 +131,7 @@ storage_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -122,7 +141,7 @@ storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -132,7 +151,7 @@ storage_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -140,7 +159,7 @@ storage_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -155,7 +174,7 @@ storage_beginscan_sampling(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-storage_rescan(HeapScanDesc scan,
+storage_rescan(StorageScanDesc scan,
ScanKey key)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
@@ -171,7 +190,7 @@ storage_rescan(HeapScanDesc scan,
* ----------------
*/
void
-storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
@@ -186,7 +205,7 @@ storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
* ----------------
*/
void
-storage_endscan(HeapScanDesc scan)
+storage_endscan(StorageScanDesc scan)
{
scan->rs_rd->rd_stamroutine->scan_end(scan);
}
@@ -199,23 +218,29 @@ storage_endscan(HeapScanDesc scan)
* ----------------
*/
void
-storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot)
{
scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
}
StorageTuple
-storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+storage_getnext(StorageScanDesc sscan, ScanDirection direction)
{
return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
}
TupleTableSlot *
-storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
}
+StorageTuple
+storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_fetch_tuple_from_offset(sscan, blkno, offset);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 26a9ccb657..ee9352df06 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -478,10 +478,10 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
}
else
{
- HeapScanDesc scan = sysscan->scan;
+ StorageScanDesc scan = sysscan->scan;
Assert(IsMVCCSnapshot(scan->rs_snapshot));
- Assert(tup == &scan->rs_ctup);
+ /* hari Assert(tup == &scan->rs_ctup); */
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index e270cbc4a0..8793b95c08 100644
--- a/src/backend/access/tablesample/system.c
+++ b/src/backend/access/tablesample/system.c
@@ -183,7 +183,7 @@ static BlockNumber
system_nextsampleblock(SampleScanState *node)
{
SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc scan = node->pagescan;
BlockNumber nextblock = sampler->nextblock;
uint32 hashinput[2];
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index a73a363a49..da3d48b9cc 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -577,7 +577,7 @@ boot_openrel(char *relname)
int i;
struct typmap **app;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
@@ -893,7 +893,7 @@ gettype(char *type)
{
int i;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
struct typmap **app;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d2a8a06097..281e3a8e2a 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
{
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a201b00cae..49b75478bc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1897,7 +1897,7 @@ index_update_stats(Relation rel,
ReindexIsProcessingHeap(RelationRelationId))
{
/* don't assume syscache will work */
- HeapScanDesc pg_class_scan;
+ StorageScanDesc pg_class_scan;
ScanKeyData key[1];
ScanKeyInit(&key[0],
@@ -2210,7 +2210,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
{
bool is_system_catalog;
bool checking_uniqueness;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2649,7 +2649,7 @@ IndexCheckExclusion(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2963,7 +2963,7 @@ validate_index_heapscan(Relation heapRelation,
Snapshot snapshot,
v_i_state *state)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index a5a5268d12..259129d456 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1017,7 +1017,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
Snapshot snapshot;
TupleDesc tupdesc;
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 1d048e6394..842abcc8b5 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -151,7 +151,7 @@ RemoveConversionById(Oid conversionOid)
{
Relation rel;
HeapTuple tuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scanKeyData;
ScanKeyInit(&scanKeyData,
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 517e3101cd..63324cfc8e 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -171,7 +171,7 @@ void
DropSetting(Oid databaseid, Oid roleid)
{
Relation relsetting;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 145e3c1d65..5e3915b438 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index a51f2e4dfc..050dfa3e4c 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -377,7 +377,7 @@ void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData skey[2];
HeapTuple tup;
int nkeys = 0;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e0f6973a3f..ccdbe70ff6 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -746,7 +746,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
Datum *values;
bool *isnull;
IndexScanDesc indexScan;
- HeapScanDesc heapScan;
+ StorageScanDesc heapScan;
bool use_wal;
bool is_system_catalog;
TransactionId OldestXmin;
@@ -1638,7 +1638,7 @@ static List *
get_tables_to_cluster(MemoryContext cluster_context)
{
Relation indRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData entry;
HeapTuple indexTuple;
Form_pg_index index;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 79f2085fb3..3d419ae1a9 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2030,7 +2030,7 @@ CopyTo(CopyState cstate)
{
Datum *values;
bool *nulls;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 39850b1b37..09135774c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -99,7 +99,7 @@ static int errdetail_busy_db(int notherbackends, int npreparedxacts);
Oid
createdb(ParseState *pstate, const CreatedbStmt *stmt)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
Relation rel;
Oid src_dboid;
Oid src_owner;
@@ -1872,7 +1872,7 @@ static void
remove_dbtablespaces(Oid db_id)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
@@ -1939,7 +1939,7 @@ check_db_file_conflict(Oid db_id)
{
bool result = false;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 5209083a72..b8440f8e73 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1874,7 +1874,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{
Oid objectOid;
Relation relationRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scan_keys[1];
HeapTuple tuple;
MemoryContext private_context;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3f928a8051..bdf79fe5b9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4481,7 +4481,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
@@ -5054,7 +5054,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
@@ -8205,7 +8205,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Expr *origexpr;
ExprState *exprstate;
TupleDesc tupdesc;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
MemoryContext oldcxt;
@@ -8288,7 +8288,7 @@ validateForeignKeyConstraint(char *conname,
Oid pkindOid,
Oid constraintOid)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
@@ -10740,7 +10740,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ListCell *l;
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index cdfa8ffb3f..be3c0db9e2 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -402,7 +402,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
char *tablespacename = stmt->tablespacename;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
Relation rel;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -913,7 +913,7 @@ RenameTableSpace(const char *oldname, const char *newname)
Oid tspId;
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
Form_pg_tablespace newform;
@@ -988,7 +988,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
{
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tup;
Oid tablespaceoid;
Datum datum;
@@ -1382,7 +1382,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
{
Oid result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -1428,7 +1428,7 @@ get_tablespace_name(Oid spc_oid)
{
char *result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index b95e3fc5ab..273bca0c2b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2382,7 +2382,7 @@ AlterDomainNotNull(List *names, bool notNull)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
@@ -2778,7 +2778,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bafc15eac6..16622f3bee 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -529,7 +529,7 @@ get_all_vacuum_rels(void)
{
List *vacrels = NIL;
Relation pgclass;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
@@ -1183,7 +1183,7 @@ vac_truncate_clog(TransactionId frozenXID,
{
TransactionId nextXID = ReadNewTransactionId();
Relation relation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid oldestxid_datoid;
Oid minmulti_datoid;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 9e24b1abd8..a1f1303e76 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -54,14 +54,14 @@
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
-static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
+static void bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres);
static inline void BitmapDoneInitializingSharedState(
ParallelBitmapHeapState *pstate);
static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node,
TBMIterateResult *tbmres);
static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
static inline void BitmapPrefetch(BitmapHeapScanState *node,
- HeapScanDesc scan);
+ StorageScanDesc scan);
static bool BitmapShouldInitializeSharedState(
ParallelBitmapHeapState *pstate);
@@ -76,7 +76,8 @@ static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
TIDBitmap *tbm;
TBMIterator *tbmiterator = NULL;
TBMSharedIterator *shared_tbmiterator = NULL;
@@ -92,6 +93,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scan = node->ss.ss_currentScanDesc;
+ pagescan = node->pagescan;
tbm = node->tbm;
if (pstate == NULL)
tbmiterator = node->tbmiterator;
@@ -191,8 +193,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
for (;;)
{
- Page dp;
- ItemId lp;
+ StorageTuple tuple;
CHECK_FOR_INTERRUPTS();
@@ -219,7 +220,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
- if (tbmres->blockno >= scan->rs_nblocks)
+ if (tbmres->blockno >= pagescan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
@@ -228,7 +229,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Fetch the current heap page and identify candidate tuples.
*/
- bitgetpage(scan, tbmres);
+ bitgetpage(node, tbmres);
if (tbmres->ntuples >= 0)
node->exact_pages++;
@@ -238,7 +239,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
- scan->rs_cindex = 0;
+ pagescan->rs_cindex = 0;
/* Adjust the prefetch target */
BitmapAdjustPrefetchTarget(node);
@@ -248,7 +249,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
- scan->rs_cindex++;
+ pagescan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -275,7 +276,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
- if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
+ if (pagescan->rs_cindex < 0 || pagescan->rs_cindex >= pagescan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
continue;
@@ -293,23 +294,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple
*/
- targoffset = scan->rs_vistuples[scan->rs_cindex];
- dp = (Page) BufferGetPage(scan->rs_cbuf);
- lp = PageGetItemId(dp, targoffset);
- Assert(ItemIdIsNormal(lp));
-
- scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- scan->rs_ctup.t_len = ItemIdGetLength(lp);
- scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
-
- pgstat_count_heap_fetch(scan->rs_rd);
+ targoffset = pagescan->rs_vistuples[pagescan->rs_cindex];
+ tuple = storage_fetch_tuple_from_offset(scan, tbmres->blockno, targoffset);
/*
* Set up the result slot to point to this tuple. Note that the slot
* acquires a pin on the buffer.
*/
- ExecStoreTuple(&scan->rs_ctup,
+ ExecStoreTuple(tuple,
slot,
scan->rs_cbuf,
false);
@@ -350,8 +342,10 @@ BitmapHeapNext(BitmapHeapScanState *node)
* interesting according to the bitmap, and visible according to the snapshot.
*/
static void
-bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
+bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres)
{
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
BlockNumber page = tbmres->blockno;
Buffer buffer;
Snapshot snapshot;
@@ -360,7 +354,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
- Assert(page < scan->rs_nblocks);
+ Assert(page < pagescan->rs_nblocks);
scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
scan->rs_rd,
@@ -403,7 +397,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
ItemPointerSet(&tid, page, offnum);
if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
- scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
+ pagescan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
@@ -419,23 +413,20 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
{
ItemId lp;
- HeapTupleData loctup;
+ StorageTuple loctup;
bool valid;
lp = PageGetItemId(dp, offnum);
if (!ItemIdIsNormal(lp))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- loctup.t_len = ItemIdGetLength(lp);
- loctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ loctup = storage_fetch_tuple_from_offset(scan, page, offnum);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, loctup, snapshot, buffer);
if (valid)
{
- scan->rs_vistuples[ntup++] = offnum;
- PredicateLockTuple(scan->rs_rd, &loctup, snapshot);
+ pagescan->rs_vistuples[ntup++] = offnum;
+ PredicateLockTuple(scan->rs_rd, loctup, snapshot);
}
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_rd, loctup,
buffer, snapshot);
}
}
@@ -443,7 +434,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ pagescan->rs_ntuples = ntup;
}
/*
@@ -569,7 +560,7 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
* BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
*/
static inline void
-BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
+BitmapPrefetch(BitmapHeapScanState *node, StorageScanDesc scan)
{
#ifdef USE_PREFETCH
ParallelBitmapHeapState *pstate = node->pstate;
@@ -724,7 +715,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -871,6 +862,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
0,
NULL);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
+
/*
* get the scan type from the relation descriptor.
*/
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 64f3dd8be0..064b655aea 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -31,9 +31,7 @@ static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
static StorageTuple tablesample_getnext(SampleScanState *scanstate);
static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
-
-//hari
+ SampleScanState *scanstate);
/* ----------------------------------------------------------------
* Scan Support
@@ -357,6 +355,7 @@ tablesample_init(SampleScanState *scanstate)
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
}
else
{
@@ -382,10 +381,11 @@ static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+ StorageTuple tuple;
Snapshot snapshot = scan->rs_snapshot;
- bool pagemode = scan->rs_pageatatime;
+ bool pagemode = pagescan->rs_pageatatime;
BlockNumber blockno;
Page page;
bool all_visible;
@@ -396,10 +396,9 @@ tablesample_getnext(SampleScanState *scanstate)
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
{
Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
return NULL;
}
if (tsm->NextSampleBlock)
@@ -407,13 +406,12 @@ tablesample_getnext(SampleScanState *scanstate)
blockno = tsm->NextSampleBlock(scanstate);
if (!BlockNumberIsValid(blockno))
{
- tuple->t_data = NULL;
return NULL;
}
}
else
- blockno = scan->rs_startblock;
- Assert(blockno < scan->rs_nblocks);
+ blockno = pagescan->rs_startblock;
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
scan->rs_inited = true;
}
@@ -456,14 +454,12 @@ tablesample_getnext(SampleScanState *scanstate)
if (!ItemIdIsNormal(itemid))
continue;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
- tuple->t_len = ItemIdGetLength(itemid);
- ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
+ tuple = storage_fetch_tuple_from_offset(scan, blockno, tupoffset);
if (all_visible)
visible = true;
else
- visible = SampleTupleVisible(tuple, tupoffset, scan);
+ visible = SampleTupleVisible(tuple, tupoffset, scanstate);
/* in pagemode, heapgetpage did this for us */
if (!pagemode)
@@ -494,14 +490,14 @@ tablesample_getnext(SampleScanState *scanstate)
if (tsm->NextSampleBlock)
{
blockno = tsm->NextSampleBlock(scanstate);
- Assert(!scan->rs_syncscan);
+ Assert(!pagescan->rs_syncscan);
finished = !BlockNumberIsValid(blockno);
}
else
{
/* Without NextSampleBlock, just do a plain forward seqscan. */
blockno++;
- if (blockno >= scan->rs_nblocks)
+ if (blockno >= pagescan->rs_nblocks)
blockno = 0;
/*
@@ -514,10 +510,10 @@ tablesample_getnext(SampleScanState *scanstate)
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
+ if (pagescan->rs_syncscan)
ss_report_location(scan->rs_rd, blockno);
- finished = (blockno == scan->rs_startblock);
+ finished = (blockno == pagescan->rs_startblock);
}
/*
@@ -529,12 +525,11 @@ tablesample_getnext(SampleScanState *scanstate)
ReleaseBuffer(scan->rs_cbuf);
scan->rs_cbuf = InvalidBuffer;
scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
scan->rs_inited = false;
return NULL;
}
- Assert(blockno < scan->rs_nblocks);
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
/* Re-establish state for new page */
@@ -549,16 +544,19 @@ tablesample_getnext(SampleScanState *scanstate)
/* Count successfully-fetched tuples as heap fetches */
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return tuple;
}
/*
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, SampleScanState *scanstate)
{
- if (scan->rs_pageatatime)
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+
+ if (pagescan->rs_pageatatime)
{
/*
* In pageatatime mode, heapgetpage() already did visibility checks,
@@ -570,12 +568,12 @@ SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan
* gain to justify the restriction.
*/
int start = 0,
- end = scan->rs_ntuples - 1;
+ end = pagescan->rs_ntuples - 1;
while (start <= end)
{
int mid = (start + end) / 2;
- OffsetNumber curoffset = scan->rs_vistuples[mid];
+ OffsetNumber curoffset = pagescan->rs_vistuples[mid];
if (tupoffset == curoffset)
return true;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 0521df1fa9..ac3754d91d 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -317,9 +317,10 @@ void
ExecSeqScanReInitializeDSM(SeqScanState *node,
ParallelContext *pcxt)
{
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ ParallelHeapScanDesc pscan;
- heap_parallelscan_reinitialize(scan->rs_parallel);
+ pscan = storageam_get_parallelheapscandesc(node->ss.ss_currentScanDesc);
+ heap_parallelscan_reinitialize(pscan);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 9d35a41e22..aa7808001d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xlog.h"
@@ -253,7 +254,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amsearchnulls = amroutine->amsearchnulls;
info->amcanparallel = amroutine->amcanparallel;
info->amhasgettuple = (amroutine->amgettuple != NULL);
- info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amhasgetbitmap = ((amroutine->amgetbitmap != NULL)
+ && (relation->rd_stamroutine->scan_get_heappagescandesc != NULL));
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
@@ -1825,7 +1827,7 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
Relation relation)
{
PartitionDesc partdesc;
- PartitionKey partkey;
+ PartitionKey partkey;
Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
@@ -1890,8 +1892,8 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
/*
* Did not find matching partition scheme. Create one copying relevant
- * information from the relcache. We need to copy the contents of the array
- * since the relcache entry may not survive after we have closed the
+ * information from the relcache. We need to copy the contents of the
+ * array since the relcache entry may not survive after we have closed the
* relation.
*/
part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData));
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0d2e1733bf..ee005d4168 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1865,7 +1865,7 @@ get_database_list(void)
{
List *dblist = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
@@ -1931,7 +1931,7 @@ do_autovacuum(void)
{
Relation classRel;
HeapTuple tuple;
- HeapScanDesc relScan;
+ StorageScanDesc relScan;
Form_pg_database dbForm;
List *table_oids = NIL;
List *orphan_oids = NIL;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 36808c4067..db4b091471 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1207,7 +1207,7 @@ pgstat_collect_oids(Oid catalogid)
HTAB *htab;
HASHCTL hash_ctl;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 83ec2dfcbe..73bb1770f1 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -107,7 +107,7 @@ get_subscription_list(void)
{
List *res = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4924daca76..7122987ad7 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -420,7 +420,7 @@ DefineQueryRewrite(char *rulename,
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
Snapshot snapshot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 03b7cc76d7..42a35824e5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1208,7 +1208,7 @@ static bool
ThereIsAtLeastOneRole(void)
{
Relation pg_authid_rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
bool result;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index db87b686c5..04aa102ab1 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -97,6 +97,8 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
#define heap_close(r,l) relation_close(r,l)
/* struct definitions appear in relscan.h */
+typedef struct HeapPageScanDescData *HeapPageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
@@ -106,7 +108,7 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
+extern void heapgetpage(StorageScanDesc sscan, BlockNumber page);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 147f862a2b..2651fd73d7 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/htup_details.h"
#include "access/itup.h"
#include "access/tupdesc.h"
@@ -42,40 +43,54 @@ typedef struct ParallelHeapScanDescData
char phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelHeapScanDescData;
-typedef struct HeapScanDescData
+typedef struct StorageScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
Snapshot rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
ScanKey rs_key; /* array of scan key descriptors */
- bool rs_bitmapscan; /* true if this is really a bitmap scan */
- bool rs_samplescan; /* true if this is really a sample scan */
+
+ /* scan current state */
+ bool rs_inited; /* false = scan not init'd yet */
+ BlockNumber rs_cblock; /* current block # in scan, if any */
+ Buffer rs_cbuf; /* current buffer in scan, if any */
+} StorageScanDescData;
+
+typedef struct HeapPageScanDescData
+{
bool rs_pageatatime; /* verify visibility page-at-a-time? */
- bool rs_allow_strat; /* allow or disallow use of access strategy */
- bool rs_allow_sync; /* allow or disallow use of syncscan */
- bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* total number of blocks in rel */
BlockNumber rs_startblock; /* block # to start at */
BlockNumber rs_numblocks; /* max number of blocks to scan */
+
/* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */
BufferAccessStrategy rs_strategy; /* access strategy for reads */
bool rs_syncscan; /* report location to syncscan logic? */
- /* scan current state */
- bool rs_inited; /* false = scan not init'd yet */
- HeapTupleData rs_ctup; /* current tuple in scan, if any */
- BlockNumber rs_cblock; /* current block # in scan, if any */
- Buffer rs_cbuf; /* current buffer in scan, if any */
- /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- ParallelHeapScanDesc rs_parallel; /* parallel scan information */
-
/* these fields only used in page-at-a-time mode and for bitmap scans */
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
+} HeapPageScanDescData;
+
+typedef struct HeapScanDescData
+{
+ /* scan parameters */
+ StorageScanDescData rs_scan; /* */
+ HeapPageScanDescData rs_pagescan;
+ bool rs_bitmapscan; /* true if this is really a bitmap scan */
+ bool rs_samplescan; /* true if this is really a sample scan */
+ bool rs_allow_strat; /* allow or disallow use of access strategy */
+ bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
+
+ HeapTupleData rs_ctup; /* current tuple in scan, if any */
+
+ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+ ParallelHeapScanDesc rs_parallel; /* parallel scan information */
} HeapScanDescData;
/*
@@ -149,12 +164,12 @@ typedef struct ParallelIndexScanDescData
char ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelIndexScanDescData;
-/* Struct for heap-or-index scans of system tables */
+/* Struct for storage-or-index scans of system tables */
typedef struct SysScanDescData
{
Relation heap_rel; /* catalog being scanned */
Relation irel; /* NULL if doing heap scan */
- HeapScanDesc scan; /* only valid in heap-scan case */
+ StorageScanDesc scan; /* only valid in storage-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index ec5dc89a26..4c1ff4cea5 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -20,7 +20,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
-typedef void *StorageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef union tuple_data
{
@@ -43,29 +43,31 @@ typedef enum tuple_data_flags
typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
bool *specConflict, List *arbiterIndexes);
-extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
-
-extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
-extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+extern ParallelHeapScanDesc storageam_get_parallelheapscandesc(StorageScanDesc sscan);
+extern HeapPageScanDesc storageam_get_heappagescandesc(StorageScanDesc sscan);
+extern void storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern StorageScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-
-extern void storage_endscan(HeapScanDesc scan);
-extern void storage_rescan(HeapScanDesc scan, ScanKey key);
-extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern StorageScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern StorageScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(StorageScanDesc scan);
+extern void storage_rescan(StorageScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
-extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
-extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_getnext(StorageScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset);
extern void storage_get_latest_tid(Relation relation,
Snapshot snapshot,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 03a6aa425a..bfe2809d6a 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -20,32 +20,37 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
-typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+typedef StorageScanDesc(*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef ParallelHeapScanDesc (*ScanGetParallelheapscandesc_function) (StorageScanDesc scan);
+typedef HeapPageScanDesc(*ScanGetHeappagescandesc_function) (StorageScanDesc scan);
+
+typedef void (*ScanSetlimits_function) (StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
/* must return a TupleTableSlot? */
-typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+typedef StorageTuple(*ScanGetnext_function) (StorageScanDesc scan,
ScanDirection direction);
-typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (StorageScanDesc scan,
ScanDirection direction, TupleTableSlot *slot);
+typedef StorageTuple(*ScanFetchTupleFromOffset_function) (StorageScanDesc scan,
+ BlockNumber blkno, OffsetNumber offset);
-typedef void (*ScanEnd_function) (HeapScanDesc scan);
+typedef void (*ScanEnd_function) (StorageScanDesc scan);
-typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
-typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+typedef void (*ScanGetpage_function) (StorageScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+typedef void (*ScanUpdateSnapshot_function) (StorageScanDesc scan, Snapshot snapshot);
typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
@@ -160,9 +165,12 @@ typedef struct StorageAmRoutine
/* Operations on relation scans */
ScanBegin_function scan_begin;
+ ScanGetParallelheapscandesc_function scan_get_parallelheapscandesc;
+ ScanGetHeappagescandesc_function scan_get_heappagescandesc;
ScanSetlimits_function scansetlimits;
ScanGetnext_function scan_getnext;
ScanGetnextSlot_function scan_getnextslot;
+ ScanFetchTupleFromOffset_function scan_fetch_tuple_from_offset;
ScanEnd_function scan_end;
ScanGetpage_function scan_getpage;
ScanRescan_function scan_rescan;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 6a3a88bdd4..5bda8a26fe 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1102,7 +1102,7 @@ typedef struct ScanState
{
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
- HeapScanDesc ss_currentScanDesc;
+ StorageScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
@@ -1123,6 +1123,7 @@ typedef struct SeqScanState
typedef struct SampleScanState
{
ScanState ss;
+ HeapPageScanDesc pagescan;
List *args; /* expr states for TABLESAMPLE params */
ExprState *repeatable; /* expr state for REPEATABLE expr */
/* use struct pointer to avoid including tsmapi.h here */
@@ -1347,6 +1348,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
+ HeapPageScanDesc pagescan;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
--
2.14.2.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.3K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/5-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From af5c2b7ee1136ac308b3caa3f1121ee0292d6e5e Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/8] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 1 +
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa0fd..33079c1c16 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4c83a63f7d..278a6e8e1c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5174,16 +5176,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..0a7e0a33e8 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b894..dd9c263ade 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031aad7..4a8ab151d7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3877,6 +3877,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..ea352fa247 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 684f7f20a8..3113966415 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1713,11 +1713,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e8fdf8454d..bb1570c94f 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1155,15 +1155,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.14.2.windows.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.7K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/6-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From 394ca0ba07e969fa83669afa5e2b11bba7992763 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/8] Storage AM API hooks and related functions
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 227 +++++++++++++++++++++++++++++++
src/include/catalog/pg_am.h | 3 +
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
9 files changed, 420 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496bcd..02a3909e7c 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000000..88827e7957
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000000..bcbe14588b
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine * HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68639..6459435c78 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000000..2b08c6f4ff
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*ScanEnd_function) (HeapScanDesc scan);
+
+
+typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*TupleInsert_function) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*TupleDelete_function) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*TupleFetch_function) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*TupleLock_function) (Relation relation,
+ ItemPointer tid,
+ StorageTuple * tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*MultiInsert_function) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*TupleFreeze_function) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*TupleGetLatestTid_function) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data(*GetTupleData_function) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple(*TupleFromDatum_function) (Datum data, Oid tableoid);
+
+typedef void (*SlotStoreTuple_function) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*SlotClearTuple_function) (TupleTableSlot *slot);
+typedef Datum (*SlotGetattr_function) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*SlotVirtualizeTuple_function) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*SlotGetTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*SpeculativeFinish_function) (Relation rel,
+ TupleTableSlot *slot);
+typedef void (*SpeculativeAbort_function) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*RelationSync_function) (Relation relation);
+
+typedef bool (*SnapshotSatisfies_function) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ SlotStoreTuple_function slot_store_tuple;
+ SlotVirtualizeTuple_function slot_virtualize_tuple;
+ SlotClearTuple_function slot_clear_tuple;
+ SlotGetattr_function slot_getattr;
+ SlotGetTuple_function slot_tuple;
+ SlotGetMinTuple_function slot_min_tuple;
+ SlotUpdateTableoid_function slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine * (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ ScanBegin_function scan_begin;
+ ScanSetlimits_function scansetlimits;
+ ScanGetnext_function scan_getnext;
+ ScanGetnextSlot_function scan_getnextslot;
+ ScanEnd_function scan_end;
+ ScanGetpage_function scan_getpage;
+ ScanRescan_function scan_rescan;
+ ScanUpdateSnapshot_function scan_update_snapshot;
+ HotSearchBuffer_function hot_search_buffer; /* heap_hot_search_buffer */
+
+ /* Operations on physical tuples */
+ TupleInsert_function tuple_insert; /* heap_insert */
+ TupleUpdate_function tuple_update; /* heap_update */
+ TupleDelete_function tuple_delete; /* heap_delete */
+ TupleFetch_function tuple_fetch; /* heap_fetch */
+ TupleLock_function tuple_lock; /* heap_lock_tuple */
+ MultiInsert_function multi_insert; /* heap_multi_insert */
+ TupleFreeze_function tuple_freeze; /* heap_freeze_tuple */
+ TupleGetLatestTid_function tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ GetTupleData_function get_tuple_data;
+ TupleFromDatum_function tuple_from_datum;
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no
+ * need for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ SpeculativeFinish_function speculative_finish;
+ SpeculativeAbort_function speculative_abort;
+
+
+ RelationSync_function relation_sync; /* heap_sync */
+
+ SnapshotSatisfies_function snapshot_satisfies;
+ SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine * GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine * GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index dd9c263ade..2c3e33c104 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -84,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4a8ab151d7..f1d0198848 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -558,6 +558,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..805d623cf0 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -497,6 +497,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 96eaf01ca0..4fda7f8384 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
2.14.2.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/7-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From a811356acd49c83218b8fed53954263f09130d76 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/8] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 119 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 130 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..179906a16f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,27 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1886,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2026,6 +2109,11 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
RelationInitPhysicalAddr(relation);
+ /*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
/*
* initialize the rel-has-index flag, using hardwired knowledge
*/
@@ -2354,6 +2442,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3458,14 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3987,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert(relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5717,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5380..82d8a534b1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -160,6 +160,12 @@ typedef struct RelationData
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
+ /*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
/*
* index access support info (used only for an index relation)
*
@@ -427,6 +433,12 @@ typedef struct ViewOptions
*/
#define RelationGetDescr(relation) ((relation)->rd_att)
+/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
/*
* RelationGetRelationName
* Returns the rel's name.
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cefe4b..03d996f5a1 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.14.2.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (148.6K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/8-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From a46e2701bee092245d53a17f69b4aa84cb4d0359 Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:38:28 +1000
Subject: [PATCH 4/8] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 10 +-
contrib/pgrowlocks/pgrowlocks.c | 7 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 57 +-
src/backend/access/heap/heapam_common.c | 160 +++
src/backend/access/heap/heapam_storage.c | 1644 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 6 +-
src/backend/commands/analyze.c | 6 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 6 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 11 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1807 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/access/htup.h | 7 +-
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 65 +-
29 files changed, 2014 insertions(+), 1928 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575d9f..10d47cd46f 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -51,7 +51,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +656,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +681,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +739,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65bd2..830e74fd07 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,9 +150,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
- GetCurrentCommandId(false),
- scan->rs_cbuf);
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
+ GetCurrentCommandId(false),
+ scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
infomask = tuple->t_data->t_infomask;
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf06138a5..284eabc970 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7ca1bb24d2..e098202f84 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909e7c..e6bc18e5ea 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 52dda41cc4..0ee8c53282 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -954,23 +957,29 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (key != NULL)
+ if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
{
- bool valid;
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ return;
+ }
+ }
+ else
{
scan->rs_cindex = lineindex;
return;
}
}
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
/*
* otherwise move to the next item on the page
@@ -1831,7 +1840,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return heap_copytuple(&(scan->rs_ctup));
}
/*
@@ -1950,7 +1959,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2096,7 +2105,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2270,7 +2279,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -2303,7 +2312,7 @@ heap_get_latest_tid(Relation relation,
bool
HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
{
- TransactionId xmin = HeapTupleHeaderGetXmin(htup);
+ TransactionId xmin = HeapTupleHeaderGetXmin(htup);
/*
* If the xmax of the old tuple is identical to the xmin of the new one,
@@ -2313,8 +2322,8 @@ HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
return true;
/*
- * If the Xmin that was in effect prior to a freeze matches the Xmax,
- * it's good too.
+ * If the Xmin that was in effect prior to a freeze matches the Xmax, it's
+ * good too.
*/
if (HeapTupleHeaderXminFrozen(htup) &&
TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup), xmax))
@@ -3140,7 +3149,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3251,7 +3260,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3711,7 +3720,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3892,7 +3901,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4643,7 +4652,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000000..4cc3eeff0c
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,160 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 88827e7957..f7e11d4946 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,12 +48,1655 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
+ */
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
+ Buffer buffer)
+{
+ return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+ != HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except when it's known the
+ * XID could not be ours anyway.
+ */
+bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
+
+static bool
+HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ switch (snapshot->visibility_type)
+ {
+ case MVCC_VISIBILITY:
+ return HeapTupleSatisfiesMVCC(stup, snapshot, buffer);
+ break;
+ case SELF_VISIBILITY:
+ return HeapTupleSatisfiesSelf(stup, snapshot, buffer);
+ break;
+ case ANY_VISIBILITY:
+ return HeapTupleSatisfiesAny(stup, snapshot, buffer);
+ break;
+ case TOAST_VISIBILITY:
+ return HeapTupleSatisfiesToast(stup, snapshot, buffer);
+ break;
+ case DIRTY_VISIBILITY:
+ return HeapTupleSatisfiesDirty(stup, snapshot, buffer);
+ break;
+ case HISTORIC_MVCC_VISIBILITY:
+ return HeapTupleSatisfiesHistoricMVCC(stup, snapshot, buffer);
+ break;
+ case NON_VACUUMABLE_VISIBILTY:
+ return HeapTupleSatisfiesNonVacuumable(stup, snapshot, buffer);
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies = HeapTupleSatisfies;
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 7753ee7b12..efeb9c1d3a 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194e18..3118c2d33f 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da001a..01321a2543 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..17be9a4042 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,8 +2359,8 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
- scan->rs_cbuf))
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
+ scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead, we can ignore it */
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 760d19142e..247d9c2f56 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1083,9 +1083,9 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
- OldestXmin,
- targbuffer))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
+ OldestXmin,
+ targbuffer))
{
case HEAPTUPLE_LIVE:
sample_it = true;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e2ad..dbcc5bc172 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 172d213fdb..c89fefff9a 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -355,7 +355,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
params->log_min_duration))
{
StringInfoData buf;
- char *msgfmt;
+ char *msgfmt;
TimestampDifference(starttime, endtime, &secs, &usecs);
@@ -986,7 +986,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2151,7 +2151,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 6035b4dfd4..c34898f241 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -428,7 +428,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 0027d21253..dbc242c699 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1312,7 +1313,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
@@ -1517,8 +1518,8 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
if (mtstate->mt_partition_dispatch_info != NULL)
{
/*
- * For tuple routing among partitions, we need TupleDescs based
- * on the partition routing table.
+ * For tuple routing among partitions, we need TupleDescs based on
+ * the partition routing table.
*/
ResultRelInfo **resultRelInfos = mtstate->mt_partitions;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a836e4..6a118d1883 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index ad65b9831d..86efe3a66b 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 251a359bff..4fbad9f0f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index ba0e3ad87d..16e6755844 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa4c8..f17b1c5324 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 294ab705f1..f823da09f4 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2040,7 +2040,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2137,7 +2137,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index b7aab0dd19..0000000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1807 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleSatisfiesNonVacuumable
- *
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
- *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
- */
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
- != HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except when it's known the
- * XID could not be ours anyway.
- */
-bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000000..ff63cf3615
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6459435c78..a5268242d7 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -28,15 +28,16 @@ typedef MinimalTupleData *MinimalTuple;
typedef enum tuple_visibility_type
{
- MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
- SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
END_OF_VISIBILITY
-} tuple_visibility_type;
+} tuple_visibility_type;
/* Result codes for HeapTupleSatisfiesVacuum */
typedef enum
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc5ba..b1c94de683 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf519778df..5752ee4731 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -52,7 +52,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 4fda7f8384..3deeb71970 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,64 +43,25 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies) (tuple, snapshot, buffer))
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
-
/*
* We don't provide a static SnapshotDirty variable because it would be
* non-reentrant. Instead, users of that snapshot type should declare a
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -107,8 +69,19 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
2.14.2.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (62.7K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/9-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 8dd4bf48a550f9f3372888d9215114e79859deea Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Fri, 27 Oct 2017 16:02:46 +1100
Subject: [PATCH 5/8] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 5 +-
src/backend/commands/copy.c | 8 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 25 +-
src/backend/executor/execExprInterp.c | 35 +--
src/backend/executor/execReplication.c | 90 ++-----
src/backend/executor/execTuples.c | 268 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 660 insertions(+), 585 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528e26..aabb4f0944 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1021,111 +1022,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
}
-/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 4cc3eeff0c..6ebe0b290c 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -100,6 +100,410 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
return true;
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *) palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple) tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple) tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine *
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
/*
* HeapTupleIsSurelyDead
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index f7e11d4946..a874e243c9 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1108,7 +1108,6 @@ HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
return false;
}
-
/*
* HeapTupleSatisfiesVacuum
*
@@ -1695,8 +1694,10 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
amroutine->snapshot_satisfies = HeapTupleSatisfies;
-
amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
+
+ amroutine->slot_storageam = heapam_storage_slot_handler;
+
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8006df32a8..b56fae4e91 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -167,7 +167,7 @@ typedef struct CopyStateData
PartitionDispatch *partition_dispatch_info;
int num_dispatch; /* Number of entries in the above array */
int num_partitions; /* Number of members in the following arrays */
- ResultRelInfo **partitions; /* Per partition result relation pointers */
+ ResultRelInfo **partitions; /* Per partition result relation pointers */
TupleConversionMap **partition_tupconv_maps;
TupleTableSlot *partition_tuple_slot;
TransitionCaptureState *transition_capture;
@@ -359,7 +359,7 @@ SendCopyBegin(CopyState cstate)
pq_sendbyte(&buf, format); /* overall format */
pq_sendint16(&buf, natts);
for (i = 0; i < natts; i++)
- pq_sendint16(&buf, format); /* per-column formats */
+ pq_sendint16(&buf, format); /* per-column formats */
pq_endmessage(&buf);
cstate->copy_dest = COPY_NEW_FE;
}
@@ -392,7 +392,7 @@ ReceiveCopyBegin(CopyState cstate)
pq_sendbyte(&buf, format); /* overall format */
pq_sendint16(&buf, natts);
for (i = 0; i < natts; i++)
- pq_sendint16(&buf, format); /* per-column formats */
+ pq_sendint16(&buf, format); /* per-column formats */
pq_endmessage(&buf);
cstate->copy_dest = COPY_NEW_FE;
cstate->fe_msgbuf = makeStringInfo();
@@ -2705,7 +2705,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..213a8cccbc 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376511..b440740e28 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8d0345cd64..005bdbd023 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2289,7 +2289,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2370,7 +2370,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2728,7 +2728,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2770,7 +2770,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2879,7 +2879,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3608,9 +3608,9 @@ static void AfterTriggerExecute(AfterTriggerEvent event,
MemoryContext per_tuple_context,
TupleTableSlot *trig_tuple_slot1,
TupleTableSlot *trig_tuple_slot2);
-static AfterTriggersTableData *GetAfterTriggersTableData(Oid relid,
- CmdType cmdType);
-static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs);
+static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid,
+ CmdType cmdType);
+static void AfterTriggerFreeQuery(AfterTriggersQueryData * qs);
static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,
@@ -3882,7 +3882,7 @@ afterTriggerRestoreEventList(AfterTriggerEventList *events,
* ----------
*/
static void
-afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
+afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData * qs)
{
AfterTriggerEventChunk *target = qs->events.head;
ListCell *lc;
@@ -4006,14 +4006,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
@@ -4634,7 +4633,7 @@ AfterTriggerEndQuery(EState *estate)
* and then called again for the same query level.
*/
static void
-AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
+AfterTriggerFreeQuery(AfterTriggersQueryData * qs)
{
Tuplestorestate *ts;
List *tables;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a0f537b706..a7d20bc391 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -508,13 +508,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(innerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -526,13 +528,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+ Assert(outerslot->tts_storage != NULL);
+ /*
+ * hari
+ * Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(outerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -544,13 +547,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(scanslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fb538c0297..07640a9992 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,6 +226,7 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
+ TupleTableSlot *scanslot;
HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d166..93aeb3eb6e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -205,6 +205,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
return slot;
}
+/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert(slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,19 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +460,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +541,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +550,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +569,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +599,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +646,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +654,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +665,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +698,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +718,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,18 +729,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
@@ -768,17 +742,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6d5f..02f6c816aa 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index dbc242c699..ec5c543bdd 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -406,7 +406,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -419,7 +419,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -437,7 +437,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -746,7 +746,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -897,7 +897,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -958,7 +958,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -977,7 +977,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -991,7 +991,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1007,7 +1007,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1123,7 +1123,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index e9a5d5a1a5..9a47276274 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -58,7 +58,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
shm_mq_result result;
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index bc6d8246a7..4344a299e4 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -724,9 +724,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3615..feb35c5024 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine * heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b0d4c54121..168edb058d 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -20,6 +20,19 @@
#include "access/transam.h"
#include "storage/bufpage.h"
+/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
@@ -658,7 +671,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce4ee..a730f265dd 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.14.2.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (261.0K, ../../CAJrrPGf17rSrM_uonLpASZ3d3nLSWTPj_TD5S8yQc6OkVS2=VA@mail.gmail.com/10-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From 3406fd50f1bd8a8093722465083fe8ed10628d7b Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Sat, 28 Oct 2017 15:36:24 +1100
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM. Move the index insertion
logic into storage AM, The Index insert still outside for
the case of multi_insert (Yet to change).
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2875 ++++--------------------------
src/backend/access/heap/heapam_common.c | 2 +
src/backend/access/heap/heapam_storage.c | 2204 ++++++++++++++++++++++-
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 281 +++
src/backend/access/heap/tuptoaster.c | 9 +-
src/backend/commands/copy.c | 39 +-
src/backend/commands/createas.c | 24 +-
src/backend/commands/matview.c | 22 +-
src/backend/commands/tablecmds.c | 6 +-
src/backend/commands/trigger.c | 49 +-
src/backend/executor/execIndexing.c | 2 +-
src/backend/executor/execMain.c | 130 +-
src/backend/executor/execReplication.c | 72 +-
src/backend/executor/execTuples.c | 1 +
src/backend/executor/nodeLockRows.c | 47 +-
src/backend/executor/nodeModifyTable.c | 244 ++-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/htup_details.h | 1 +
src/include/access/storageam.h | 86 +
src/include/access/storageamapi.h | 35 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 15 +-
src/include/executor/tuptable.h | 1 +
src/include/nodes/execnodes.h | 6 +-
src/include/storage/bufmgr.h | 1 -
31 files changed, 3468 insertions(+), 2898 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index aabb4f0944..a66eed8d3b 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
return PointerGetDatum(td);
}
+/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e5ea..162736ff15 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 0ee8c53282..327ad30b2d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1843,158 +1750,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
return heap_copytuple(&(scan->rs_ctup));
}
-/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
@@ -2177,130 +1932,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !HeapTupleUpdateXmaxMatchesXmin(priorXmax, tp.t_data))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
/*
* HeapTupleUpdateXmaxMatchesXmin - verify update chain xmax/xmin lineage
*
@@ -2362,7 +1993,7 @@ HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax, HeapTupleHeader htup)
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2418,7 +2049,6 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate)
bistate->current_buf = InvalidBuffer;
}
-
/*
* heap_insert - insert tuple into a heap
*
@@ -2645,7 +2275,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2715,417 +2345,115 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
+
+ Assert(ItemPointerIsValid(tid));
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ /*
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
+ */
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
-
- /*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(page))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ if (PageIsAllVisible(page))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
/*
* If we didn't pin the visibility map page and the page has become all
@@ -4555,7 +3883,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4573,1712 +3901,333 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
+ if (*have_tuple_lock)
+ return true;
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
-
- case LockWaitError:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- *have_tuple_lock = true;
-
- return true;
-}
-
-/*
- * Given an original set of Xmax and infomask, and a transaction (identified by
- * add_to_xmax) acquiring a new lock of some mode, compute the new Xmax and
- * corresponding infomasks to use on the tuple.
- *
- * Note that this might have side effects such as creating a new MultiXactId.
- *
- * Most callers will have called HeapTupleSatisfiesUpdate before this function;
- * that will have set the HEAP_XMAX_INVALID bit if the xmax was a MultiXactId
- * but it was not running anymore. There is a race condition, which is that the
- * MultiXactId may have finished since then, but that uncommon case is handled
- * either here, or within MultiXactIdExpand.
- *
- * There is a similar race condition possible when the old xmax was a regular
- * TransactionId. We test TransactionIdIsInProgress again just to narrow the
- * window, but it's still possible to end up creating an unnecessary
- * MultiXactId. Fortunately this is harmless.
- */
-static void
-compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2)
-{
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
-
- Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
-
-l5:
- new_infomask = 0;
- new_infomask2 = 0;
- if (old_infomask & HEAP_XMAX_INVALID)
- {
- /*
- * No previous locker; we just insert our own TransactionId.
- *
- * Note that it's critical that this case be the first one checked,
- * because there are several blocks below that come back to this one
- * to implement certain optimizations; old_infomask might contain
- * other dirty bits in those cases, but we don't really care.
- */
- if (is_update)
- {
- new_xmax = add_to_xmax;
- if (mode == LockTupleExclusive)
- new_infomask2 |= HEAP_KEYS_UPDATED;
- }
- else
- {
- new_infomask |= HEAP_XMAX_LOCK_ONLY;
- switch (mode)
- {
- case LockTupleKeyShare:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
- break;
- case LockTupleShare:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_SHR_LOCK;
- break;
- case LockTupleNoKeyExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
+ case LockWaitError:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
}
+ *have_tuple_lock = true;
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
+ return true;
}
/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
+ * Given an original set of Xmax and infomask, and a transaction (identified by
+ * add_to_xmax) acquiring a new lock of some mode, compute the new Xmax and
+ * corresponding infomasks to use on the tuple.
*
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
+ * Note that this might have side effects such as creating a new MultiXactId.
*
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
+ * Most callers will have called HeapTupleSatisfiesUpdate before this function;
+ * that will have set the HEAP_XMAX_INVALID bit if the xmax was a MultiXactId
+ * but it was not running anymore. There is a race condition, which is that the
+ * MultiXactId may have finished since then, but that uncommon case is handled
+ * either here, or within MultiXactIdExpand.
*
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
+ * There is a similar race condition possible when the old xmax was a regular
+ * TransactionId. We test TransactionIdIsInProgress again just to narrow the
+ * window, but it's still possible to end up creating an unnecessary
+ * MultiXactId. Fortunately this is harmless.
*/
void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
+compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2)
{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
+ Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
+l5:
+ new_infomask = 0;
+ new_infomask2 = 0;
+ if (old_infomask & HEAP_XMAX_INVALID)
{
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ /*
+ * No previous locker; we just insert our own TransactionId.
+ *
+ * Note that it's critical that this case be the first one checked,
+ * because there are several blocks below that come back to this one
+ * to implement certain optimizations; old_infomask might contain
+ * other dirty bits in those cases, but we don't really care.
+ */
+ if (is_update)
+ {
+ new_xmax = add_to_xmax;
+ if (mode == LockTupleExclusive)
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ }
+ else
+ {
+ new_infomask |= HEAP_XMAX_LOCK_ONLY;
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+ break;
+ case LockTupleShare:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_SHR_LOCK;
+ break;
+ case LockTupleNoKeyExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
+}
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
-}
/*
* heap_inplace_update - update a tuple "in place" (ie, overwrite it)
@@ -7066,7 +5015,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7233,7 +5182,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7255,7 +5204,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7817,7 +5766,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9193,46 +7142,6 @@ heap2_redo(XLogReaderState *record)
}
}
-/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
/*
* Mask a heap page before performing consistency checks on it.
*/
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 6ebe0b290c..342c575b8c 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -24,9 +24,11 @@
#include "access/heapam.h"
#include "access/heapam_common.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
+#include "executor/tuptable.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index a874e243c9..d6352df472 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -48,6 +48,18 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+static void heapam_finish_speculative(Relation relation, TupleTableSlot *slot);
+static void heapam_abort_speculative(Relation relation, TupleTableSlot *slot);
+
/*-------------------------------------------------------------------------
*
* POSTGRES "time qualification" code, ie, tuple visibility rules.
@@ -1688,10 +1700,2196 @@ HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
}
}
-Datum
-heapam_storage_handler(PG_FUNCTION_ARGS)
+
+/*
+ * heap_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
{
- StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple->t_self));
+ tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple->t_len = ItemIdGetLength(lp);
+ tuple->t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is
+ * completely bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ if ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0))
+ {
+ Assert(IndexFunc != NULL);
+
+ if (options & HEAP_INSERT_SPECULATIVE)
+ {
+ bool specConflict = false;
+
+ *recheckIndexes = (IndexFunc) (slot, estate, true,
+ &specConflict,
+ arbiterIndexes);
+
+ /* adjust the tuple's state accordingly */
+ if (!specConflict)
+ heapam_finish_speculative(relation, slot);
+ else
+ {
+ heapam_abort_speculative(relation, slot);
+ slot->tts_specConflict = true;
+ }
+ }
+ else
+ {
+ *recheckIndexes = (IndexFunc) (slot, estate, false,
+ NULL, arbiterIndexes);
+ }
+ }
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck,
+ bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ /*
+ * Note: instead of having to update the old index tuples associated with
+ * the heap tuple, all we do is form and insert new index tuples. This is
+ * because UPDATEs are actually DELETEs and INSERTs, and index tuple
+ * deletion is done later by VACUUM (see notes in ExecDelete). All we do
+ * here is insert new index tuples. -cim 9/27/89
+ */
+
+ /*
+ * insert index entries for tuple
+ *
+ * Note: heap_update returns the tid (location) of the new tuple in the
+ * t_self field.
+ *
+ * If it's a HOT update, we mustn't insert new index entries.
+ */
+ if ((result == HeapTupleMayBeUpdated) &&
+ (estate->es_result_relation_info->ri_NumIndices > 0) &&
+ (!HeapTupleIsHeapOnly(tuple)))
+ *recheckIndexes = (IndexFunc) (slot, estate, false, NULL, NIL);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ /*
+ * Assert(slot->tts_speculativeToken != 0); This needs some update in
+ * toast
+ */
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTupleData mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !HeapTupleUpdateXmaxMatchesXmin(priorXmax, mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup.t_data->t_infomask;
+ old_infomask2 = mytup.t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup.t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup.t_data->t_infomask |= new_infomask;
+ mytup.t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !HeapTupleUpdateXmaxMatchesXmin(priorXmax, tp.t_data))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data) HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data) HeapTupleHeaderGetUpdateXid(((HeapTuple) tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data) HeapTupleHeaderGetCmin(((HeapTuple) tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data) ((HeapTuple) tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data) ((HeapTuple) tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
amroutine->snapshot_satisfies = HeapTupleSatisfies;
amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 3118c2d33f..84ac4bfade 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000000..870490d00f
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,281 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid, options,
+ bistate, IndexFunc, estate,
+ arbiterIndexes, recheckIndexes);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, estate,
+ cid, crosscheck, wait, hufd,
+ lockmode, IndexFunc, recheckIndexes);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..ba9592d055 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,13 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b56fae4e91..69af210dba 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -20,6 +20,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2704,8 +2705,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2768,19 +2767,11 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
- estate,
- false,
- NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options,
+ bistate, ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2876,7 +2867,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2909,12 +2900,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2930,10 +2921,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
recheckIndexes =
- ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
- estate, false, NULL, NIL);
+ ExecInsertIndexTuples(myslot, estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2950,8 +2940,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 213a8cccbc..9e6fb8740b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,28 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
-
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ slot->tts_tupleOid = InvalidOid;
+
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +623,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740e28..936ea9b9e5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,19 +492,22 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
-
- heap_insert(myState->transientrel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ ExecMaterializeSlot(slot);
+
+ storage_insert(myState->transientrel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -522,7 +526,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3ab808715b..e1ca60b35b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4652,7 +4653,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate,
+ NULL, NULL, NIL, NULL);
ResetExprContext(econtext);
@@ -4676,7 +4678,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 005bdbd023..680a65cad5 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2352,17 +2352,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3012,9 +3016,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3030,11 +3035,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, LockWaitBlock,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, &tuple,
+ estate->es_output_cid,
+ lockmode, LockWaitBlock,
+ false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3066,7 +3071,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3112,6 +3118,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3130,17 +3137,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3946,8 +3953,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -4020,10 +4027,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -4037,10 +4043,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189fa71..ab533cf9c7 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -269,12 +269,12 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
*/
List *
ExecInsertIndexTuples(TupleTableSlot *slot,
- ItemPointer tupleid,
EState *estate,
bool noDupErr,
bool *specConflict,
List *arbiterIndexes)
{
+ ItemPointer tupleid = &slot->tts_tid;
List *result = NIL;
ResultRelInfo *resultRelInfo;
int i;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 638a856dc3..0467e17e70 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1894,7 +1894,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1974,7 +1974,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2021,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2480,7 +2480,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2497,7 +2498,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2528,7 +2531,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2561,14 +2564,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2576,12 +2579,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2595,7 +2598,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
+ if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, ((HeapTuple) tuple)->t_data))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2616,7 +2619,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2645,20 +2649,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, wait_policy,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, tuple,
+ estate->es_output_cid,
+ lockmode, wait_policy,
+ false, &buffer, &hufd);
/* We now have two pins on the buffer, get rid of one */
ReleaseBuffer(buffer);
@@ -2694,12 +2701,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2721,10 +2731,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2733,7 +2739,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2742,7 +2748,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, tuple.t_data))
+ if (!HeapTupleUpdateXmaxMatchesXmin(priorXmax, ((HeapTuple) tuple)->t_data))
{
ReleaseBuffer(buffer);
return NULL;
@@ -2760,7 +2766,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2768,17 +2776,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2824,7 +2834,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2843,7 +2853,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2871,7 +2881,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2902,8 +2912,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2927,11 +2935,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
- erm,
- datum,
- &updated);
- if (copyTuple == NULL)
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ erm,
+ datum,
+ &updated);
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2945,23 +2953,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2971,19 +2974,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3149,8 +3145,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 07640a9992..24b5ff7298 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,6 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -354,19 +355,12 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can inspect. */
- tuple = ExecHeapifySlot(slot);
-
- /* OK, store the tuple and create index entries for it */
- simple_heap_insert(rel, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot,
+ GetCurrentCommandId(true), 0, NULL,
+ ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +384,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -415,22 +409,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ HeapUpdateFailureData hufd;
+ LockTupleMode lockmode;
+ InsertIndexTuples IndexFunc = ExecInsertIndexTuples;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can write. */
- tuple = ExecHeapifySlot(slot);
+ storage_update(rel, tid, slot, estate, GetCurrentCommandId(true), InvalidSnapshot,
+ true, &hufd, &lockmode, IndexFunc, &recheckIndexes);
- /* OK, update the tuple and index entries for it */
- simple_heap_update(rel, tid, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, tid,
- estate, false, NULL,
- NIL);
+ tuple = ExecHeapifySlot(slot);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 93aeb3eb6e..d618b3d903 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -83,6 +83,7 @@
#include "access/heapam_common.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..2da5240d24 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple) (&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
- estate->es_output_cid,
- lockmode, erm->waitPolicy, true,
- &buffer, &hufd);
- ReleaseBuffer(buffer);
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
+ estate->es_output_cid,
+ lockmode, erm->waitPolicy, true,
+ &buffer, &hufd);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index ec5c543bdd..965c629870 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
- Relation rel = relinfo->ri_RelationDesc;
Buffer buffer;
- HeapTupleData tuple;
+ Relation rel = relinfo->ri_RelationDesc;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -267,12 +271,6 @@ ExecInsert(ModifyTableState *mtstate,
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
/*
* get information on the (current) result relation
*/
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -374,19 +374,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where
+ * the plan tree can return a tuple extracted literally from some table
+ * with the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -404,9 +416,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -418,9 +427,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -436,14 +442,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -463,7 +467,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -493,7 +498,6 @@ ExecInsert(ModifyTableState *mtstate,
/* Perform a speculative insertion. */
uint32 specToken;
ItemPointerData conflictTid;
- bool specConflict;
/*
* Do a non-conclusive check for conflicts first.
@@ -508,7 +512,7 @@ ExecInsert(ModifyTableState *mtstate,
* speculatively.
*/
vlock:
- specConflict = false;
+ slot->tts_specConflict = false;
if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
arbiterIndexes))
{
@@ -554,24 +558,17 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- HEAP_INSERT_SPECULATIVE,
- NULL);
-
- /* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, true, &specConflict,
- arbiterIndexes);
-
- /* adjust the tuple's state accordingly */
- if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
- else
- heap_abort_speculative(resultRelationDesc, tuple);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ HEAP_INSERT_SPECULATIVE,
+ NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -587,7 +584,7 @@ ExecInsert(ModifyTableState *mtstate,
* the pre-check again, which will now find the conflicting tuple
* (unless it aborts before we get there).
*/
- if (specConflict)
+ if (slot->tts_specConflict)
{
list_free(recheckIndexes);
goto vlock;
@@ -599,19 +596,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- 0, NULL);
-
- /* insert index entries for tuple */
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- arbiterIndexes);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ 0, NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
}
}
@@ -619,11 +611,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -674,7 +666,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -719,8 +711,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -746,8 +736,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -761,11 +753,11 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd);
+ result = storage_delete(resultRelationDesc, tupleid,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -861,7 +853,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -875,20 +867,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -897,7 +888,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -934,14 +925,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1006,14 +997,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1023,7 +1014,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1059,11 +1050,14 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd, &lockmode);
+ result = storage_update(resultRelationDesc, tupleid, slot,
+ estate,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd, &lockmode,
+ ExecInsertIndexTuples,
+ &recheckIndexes);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -1134,26 +1128,6 @@ lreplace:;
elog(ERROR, "unrecognized heap_update status: %u", result);
return NULL;
}
-
- /*
- * Note: instead of having to update the old index tuples associated
- * with the heap tuple, all we do is form and insert new index tuples.
- * This is because UPDATEs are actually DELETEs and INSERTs, and index
- * tuple deletion is done later by VACUUM (see notes in ExecDelete).
- * All we do here is insert new index tuples. -cim 9/27/89
- */
-
- /*
- * insert index entries for tuple
- *
- * Note: heap_update returns the tid (location) of the new tuple in
- * the t_self field.
- *
- * If it's a HOT update, we mustn't insert new index entries.
- */
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL, NIL);
}
if (canSetTag)
@@ -1211,11 +1185,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1226,10 +1201,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1254,7 +1227,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1285,7 +1259,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1313,10 +1289,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1331,7 +1307,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1371,12 +1349,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1579,7 +1559,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..47d8b7b12f 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -331,12 +332,6 @@ TidNext(TidScanState *node)
tidList = node->tss_TidList;
numTids = node->tss_NumTids;
- /*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
/*
* Initialize or advance scan position, depending on direction.
*/
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ /* hari */
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 854097dd58..c4481ecee9 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -352,7 +353,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -387,7 +388,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 9f4367d704..b1cb80c251 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -133,17 +133,13 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern bool HeapTupleUpdateXmaxMatchesXmin(TransactionId xmax,
@@ -153,23 +149,6 @@ extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -182,7 +161,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index feb35c5024..14aad60546 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine * heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 168edb058d..489aa78731 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -816,6 +816,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000000..447935ba10
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+#include "nodes/execnodes.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+/* Function pointer to let the index tuple insert from storage am */
+typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 2b08c6f4ff..03a6aa425a 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -15,30 +15,11 @@
#include "access/heapam.h"
#include "access/sdir.h"
#include "access/skey.h"
+#include "access/storageam.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
@@ -70,11 +51,9 @@ typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
-typedef Oid (*TupleInsert_function) (Relation relation,
- TupleTableSlot *tupslot,
- CommandId cid,
- int options,
- BulkInsertState bistate);
+typedef Oid (*TupleInsert_function) (Relation rel, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
typedef HTSU_Result (*TupleDelete_function) (Relation relation,
ItemPointer tid,
@@ -86,11 +65,14 @@ typedef HTSU_Result (*TupleDelete_function) (Relation relation,
typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
ItemPointer otid,
TupleTableSlot *slot,
+ EState *estate,
CommandId cid,
Snapshot crosscheck,
bool wait,
HeapUpdateFailureData *hufd,
- LockTupleMode *lockmode);
+ LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc,
+ List **recheckIndexes);
typedef bool (*TupleFetch_function) (Relation relation,
ItemPointer tid,
@@ -209,7 +191,6 @@ typedef struct StorageAmRoutine
* need for a storage AM method, but after inserting a tuple containing a
* speculative token, the insertion must be completed by these routines:
*/
- SpeculativeFinish_function speculative_finish;
SpeculativeAbort_function speculative_abort;
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index adbcfa1297..203371148c 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index c4ecf0d50f..85b1e15093 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,16 +196,16 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
- int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
- TransactionId priorXmax);
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
+ int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
+ TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
@@ -528,9 +528,8 @@ extern int ExecCleanTargetListLength(List *targetlist);
*/
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
-extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate, bool noDupErr, bool *specConflict,
- List *arbiterIndexes);
+extern List *ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
ItemPointer conflictTid, List *arbiterIndexes);
extern void check_exclusion_constraint(Relation heap, Relation index,
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index a730f265dd..630899ea22 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -133,6 +133,7 @@ typedef struct TupleTableSlot
Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_specConflict; /* XXX describe */
bool tts_shouldFree;
bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 8698c8a50c..6a3a88bdd4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -1743,7 +1743,7 @@ typedef struct SharedSortInfo
{
int num_workers;
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
-} SharedSortInfo;
+} SharedSortInfo;
/* ----------------
* SortState information
@@ -2025,7 +2025,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index b1c94de683..1a403b8e21 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,7 +14,6 @@
#ifndef BUFMGR_H
#define BUFMGR_H
-#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
--
2.14.2.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-11-07 09:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-11-07 09:34 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Oct 31, 2017 at 8:59 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> Additional changes that are done in the patches compared to earlier
> patches apart from rebase.
>
Rebased patches are attached.
Regards,
Hari Babu
Fujitsu Australia
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (182.3K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/3-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 1bc5a33a92f8d0fc30ddc06d882a4c9a038c8675 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Tue, 7 Nov 2017 14:50:57 +1100
Subject: [PATCH 7/8] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 1523 ++--------------------------
src/backend/access/heap/heapam_storage.c | 1319 ++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 4 +-
src/backend/access/heap/storageam.c | 235 +++++
src/backend/access/index/genam.c | 11 +-
src/backend/access/index/indexam.c | 13 +-
src/backend/access/nbtree/nbtinsert.c | 7 +-
src/backend/bootstrap/bootstrap.c | 25 +-
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 59 +-
src/backend/catalog/partition.c | 24 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +-
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 +-
src/backend/commands/tablespace.c | 39 +-
src/backend/commands/trigger.c | 3 +-
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 17 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 15 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 19 +-
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 14 +-
src/backend/executor/nodeIndexonlyscan.c | 8 +-
src/backend/executor/nodeIndexscan.c | 16 +-
src/backend/executor/nodeSamplescan.c | 34 +-
src/backend/executor/nodeSeqscan.c | 45 +-
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/postmaster/autovacuum.c | 18 +-
src/backend/postmaster/pgstat.c | 7 +-
src/backend/postmaster/postmaster.c | 4 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 30 +-
src/include/access/heapam_common.h | 8 +
src/include/access/storageam.h | 39 +
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 12 +-
src/include/executor/tqueue.h | 4 +-
src/include/funcapi.h | 2 +-
57 files changed, 1996 insertions(+), 1796 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 830e74fd07..bc8b423975 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index e098202f84..c4b10d6efc 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d91452963a..6c1224db42 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -81,19 +81,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-static void heap_parallelscan_startblock_init(HeapScanDesc scan);
-static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -112,139 +99,6 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-/* ----------------------------------------------------------------
- * heap support routines
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * initscan - scan code common to heap_beginscan and heap_rescan
- * ----------------
- */
-static void
-initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
-{
- bool allow_strat;
- bool allow_sync;
-
- /*
- * Determine the number of blocks we have to scan.
- *
- * It is sufficient to do this once at scan start, since any tuples added
- * while the scan is in progress will be invisible to my snapshot anyway.
- * (That is not true when using a non-MVCC snapshot. However, we couldn't
- * guarantee to return tuples added after scan start anyway, since they
- * might go into pages we already scanned. To guarantee consistent
- * results for a non-MVCC snapshot, the caller must hold some higher-level
- * lock that ensures the interesting tuple(s) won't change.)
- */
- if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
- else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
-
- /*
- * If the table is large relative to NBuffers, use a bulk-read access
- * strategy and enable synchronized scanning (see syncscan.c). Although
- * the thresholds for these features could be different, we make them the
- * same so that there are only two behaviors to tune rather than four.
- * (However, some callers need to be able to disable one or both of these
- * behaviors, independently of the size of the table; also there is a GUC
- * variable that can disable synchronized scanning.)
- *
- * Note that heap_parallelscan_initialize has a very similar test; if you
- * change this, consider changing that one, too.
- */
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
- {
- allow_strat = scan->rs_allow_strat;
- allow_sync = scan->rs_allow_sync;
- }
- else
- allow_strat = allow_sync = false;
-
- if (allow_strat)
- {
- /* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
- }
- else
- {
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
- }
-
- if (scan->rs_parallel != NULL)
- {
- /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
- }
- else if (keep_startblock)
- {
- /*
- * When rescanning, we want to keep the previous startblock setting,
- * so that rewinding a cursor doesn't generate surprising results.
- * Reset the active syncscan setting, though.
- */
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
- }
- else if (allow_sync && synchronize_seqscans)
- {
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- }
- else
- {
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
- }
-
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
- scan->rs_ctup.t_data = NULL;
- ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
-
- /* page-at-a-time fields are always invalid when not rs_inited */
-
- /*
- * copy the scan key, if appropriate
- */
- if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
-
- /*
- * Currently, we don't have a stats counter for bitmap heap scans (but the
- * underlying bitmap index scans will be counted) or sample scans (we only
- * update stats for tuple fetches there)
- */
- if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
-}
-
-/*
- * heap_setscanlimits - restrict range of a heapscan
- *
- * startBlk is the page to start at
- * numBlks is number of pages to scan (InvalidBlockNumber means "all")
- */
-void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
-{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
-
- /* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
-
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
-}
-
/*
* heapgetpage - subroutine for heapgettup()
*
@@ -363,621 +217,6 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
scan->rs_ntuples = ntup;
}
-/* ----------------
- * heapgettup - fetch next heap tuple
- *
- * Initialize the scan if not already done; then advance to the next
- * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
- * or set scan->rs_ctup.t_data = NULL if no more tuples.
- *
- * dir == NoMovementScanDirection means "re-fetch the tuple indicated
- * by scan->rs_ctup".
- *
- * Note: the reason nkeys/key are passed separately, even though they are
- * kept in the scan descriptor, is that the caller may not want us to check
- * the scankeys.
- *
- * Note: when we fall off the end of the scan in either direction, we
- * reset rs_inited. This means that a further request with the same
- * scan direction will restart the scan, which is a bit odd, but a
- * request with the opposite scan direction will start a fresh scan
- * in the proper direction. The latter is required behavior for cursors,
- * while the former case is generally undefined behavior in Postgres
- * so we don't care too much.
- * ----------------
- */
-static void
-heapgettup(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineoff, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineoff = /* next offnum */
- OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
- /* page and lineoff now reference the physically next tid */
-
- linesleft = lines - lineoff + 1;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber(dp);
-
- if (!scan->rs_inited)
- {
- lineoff = lines; /* final offnum */
- scan->rs_inited = true;
- }
- else
- {
- lineoff = /* previous offnum */
- OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
- }
- /* page and lineoff now reference the physically previous tid */
-
- linesleft = lineoff;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- lpp = PageGetItemId(dp, lineoff);
- for (;;)
- {
- while (linesleft > 0)
- {
- if (ItemIdIsNormal(lpp))
- {
- bool valid;
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
- tuple,
- snapshot,
- scan->rs_cbuf);
-
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
-
- if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
-
- if (valid)
- {
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- {
- --lpp; /* move back in this page's ItemId array */
- --lineoff;
- }
- else
- {
- ++lpp; /* move forward in this page's ItemId array */
- ++lineoff;
- }
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * advance to next/prior page and detect end of scan
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
-
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
- lines = PageGetMaxOffsetNumber((Page) dp);
- linesleft = lines;
- if (backward)
- {
- lineoff = lines;
- lpp = PageGetItemId(dp, lines);
- }
- else
- {
- lineoff = FirstOffsetNumber;
- lpp = PageGetItemId(dp, FirstOffsetNumber);
- }
- }
-}
-
-/* ----------------
- * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
- *
- * Same API as heapgettup, but used in page-at-a-time mode
- *
- * The internal logic is much the same as heapgettup's too, but there are some
- * differences: we do not take the buffer content lock (that only needs to
- * happen inside heapgetpage), and we iterate through just the tuples listed
- * in rs_vistuples[] rather than all tuples on the page. Notice that
- * lineindex is 0-based, where the corresponding loop variable lineoff in
- * heapgettup is 1-based.
- * ----------------
- */
-static void
-heapgettup_pagemode(HeapScanDesc scan,
- ScanDirection dir,
- int nkeys,
- ScanKey key)
-{
- HeapTuple tuple = &(scan->rs_ctup);
- bool backward = ScanDirectionIsBackward(dir);
- BlockNumber page;
- bool finished;
- Page dp;
- int lines;
- int lineindex;
- OffsetNumber lineoff;
- int linesleft;
- ItemId lpp;
-
- /*
- * calculate next starting lineindex, given scan direction
- */
- if (ScanDirectionIsForward(dir))
- {
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- if (scan->rs_parallel != NULL)
- {
- heap_parallelscan_startblock_init(scan);
-
- page = heap_parallelscan_nextpage(scan);
-
- /* Other processes might have already finished the scan. */
- if (page == InvalidBlockNumber)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
- }
- else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
- lineindex = 0;
- scan->rs_inited = true;
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- /* page and lineindex now reference the next visible tid */
-
- linesleft = lines - lineindex;
- }
- else if (backward)
- {
- /* backward parallel scan not supported */
- Assert(scan->rs_parallel == NULL);
-
- if (!scan->rs_inited)
- {
- /*
- * return null immediately if relation is empty
- */
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- /*
- * Disable reporting to syncscan logic in a backwards scan; it's
- * not very likely anyone else is doing the same thing at the same
- * time, and much more likely that we'll just bollix things for
- * forward scanners.
- */
- scan->rs_syncscan = false;
- /* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
- else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
- }
- else
- {
- /* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- }
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
-
- if (!scan->rs_inited)
- {
- lineindex = lines - 1;
- scan->rs_inited = true;
- }
- else
- {
- lineindex = scan->rs_cindex - 1;
- }
- /* page and lineindex now reference the previous visible tid */
-
- linesleft = lineindex + 1;
- }
- else
- {
- /*
- * ``no movement'' scan direction: refetch prior tuple
- */
- if (!scan->rs_inited)
- {
- Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
- return;
- }
-
- page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
-
- /* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
-
- /* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
-
- return;
- }
-
- /*
- * advance the scan until we find a qualifying tuple or run out of stuff
- * to scan
- */
- for (;;)
- {
- while (linesleft > 0)
- {
- lineoff = scan->rs_vistuples[lineindex];
- lpp = PageGetItemId(dp, lineoff);
- Assert(ItemIdIsNormal(lpp));
-
- tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
- tuple->t_len = ItemIdGetLength(lpp);
- ItemPointerSet(&(tuple->t_self), page, lineoff);
-
- /*
- * if current tuple qualifies, return it.
- */
- if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
- {
- /*
- * if current tuple qualifies, return it.
- */
- if (key != NULL)
- {
- bool valid;
-
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
- {
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
- else
- {
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
- return;
- }
- }
-
- /*
- * otherwise move to the next item on the page
- */
- --linesleft;
- if (backward)
- --lineindex;
- else
- ++lineindex;
- }
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /*
- * if we get here, it means we've exhausted the items on this page and
- * it's time to move to the next.
- */
- if (backward)
- {
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
- if (page == 0)
- page = scan->rs_nblocks;
- page--;
- }
- else if (scan->rs_parallel != NULL)
- {
- page = heap_parallelscan_nextpage(scan);
- finished = (page == InvalidBlockNumber);
- }
- else
- {
- page++;
- if (page >= scan->rs_nblocks)
- page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
-
- /*
- * Report our new scan position for synchronization purposes. We
- * don't do that when moving backwards, however. That would just
- * mess up any other forward-moving scanners.
- *
- * Note: we do this before checking for end of scan so that the
- * final state of the position hint is back at the start of the
- * rel. That's not strictly necessary, but otherwise when you run
- * the same query multiple times the starting position would shift
- * a little bit backwards on every invocation, which is confusing.
- * We don't guarantee any specific ordering in general, though.
- */
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
- }
-
- /*
- * return NULL if we've exhausted all the pages
- */
- if (finished)
- {
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
- scan->rs_inited = false;
- return;
- }
-
- heapgetpage(scan, page);
-
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
- linesleft = lines;
- if (backward)
- lineindex = lines - 1;
- else
- lineindex = 0;
- }
-}
-
-
#if defined(DISABLE_COMPLEX_MACRO)
/*
* This is formatted so oddly so that the correspondence to the macro
@@ -1200,320 +439,99 @@ relation_close(Relation relation, LOCKMODE lockmode)
RelationClose(relation);
if (lockmode != NoLock)
- UnlockRelationId(&relid, lockmode);
-}
-
-
-/* ----------------
- * heap_open - open a heap relation by relation OID
- *
- * This is essentially relation_open plus check that the relation
- * is not an index nor a composite type. (The caller should also
- * check that it's not a view or foreign table before assuming it has
- * storage.)
- * ----------------
- */
-Relation
-heap_open(Oid relationId, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_open(relationId, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
-{
- Relation r;
-
- r = relation_openrv(relation, lockmode);
-
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
-
- return r;
-}
-
-/* ----------------
- * heap_openrv_extended - open a heap relation specified
- * by a RangeVar node
- *
- * As above, but optionally return NULL instead of failing for
- * relation-not-found.
- * ----------------
- */
-Relation
-heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok)
-{
- Relation r;
-
- r = relation_openrv_extended(relation, lockmode, missing_ok);
-
- if (r)
- {
- if (r->rd_rel->relkind == RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is an index",
- RelationGetRelationName(r))));
- else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is a composite type",
- RelationGetRelationName(r))));
- }
-
- return r;
-}
-
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to TRUE with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
-{
- HeapScanDesc scan;
-
- /*
- * increment relation ref count while scanning relation
- *
- * This is just to make really sure the relcache entry won't go away while
- * the scan has a pointer to it. Caller should be holding the rel open
- * anyway, so this is redundant in all normal scenarios...
- */
- RelationIncrementReferenceCount(relation);
-
- /*
- * allocate and initialize scan descriptor
- */
- scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
-
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
- scan->rs_bitmapscan = is_bitmapscan;
- scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_temp_snap = temp_snap;
- scan->rs_parallel = parallel_scan;
-
- /*
- * we can use page-at-a-time mode if it's an MVCC-safe snapshot
- */
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
-
- /*
- * For a seqscan in a serializable transaction, acquire a predicate lock
- * on the entire relation. This is required not only to lock all the
- * matching tuples, but also to conflict with new insertions into the
- * table. In an indexscan, we take page locks on the index pages covering
- * the range specified in the scan qual, but in a heap scan there is
- * nothing more fine-grained to lock. A bitmap scan is a different story,
- * there we have already scanned the index and locked the index pages
- * covering the predicate. But in that case we still have to lock any
- * matching heap tuples.
- */
- if (!is_bitmapscan)
- PredicateLockRelation(relation, snapshot);
-
- /* we only need to set this up once */
- scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
-
- /*
- * we do this here instead of in initscan() because heap_rescan also calls
- * initscan() and we don't want to allocate memory again
- */
- if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
- else
- scan->rs_key = NULL;
-
- initscan(scan, key, false);
-
- return scan;
+ UnlockRelationId(&relid, lockmode);
}
+
/* ----------------
- * heap_rescan - restart a relation scan
+ * heap_open - open a heap relation by relation OID
+ *
+ * This is essentially relation_open plus check that the relation
+ * is not an index nor a composite type. (The caller should also
+ * check that it's not a view or foreign table before assuming it has
+ * storage.)
* ----------------
*/
-void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+Relation
+heap_open(Oid relationId, LOCKMODE lockmode)
{
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ Relation r;
- /*
- * reinitialize scan descriptor
- */
- initscan(scan, key, true);
+ r = relation_open(relationId, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
+ * heap_openrv - open a heap relation specified
+ * by a RangeVar node
*
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
+ * As above, but relation is specified by a RangeVar.
* ----------------
*/
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
+Relation
+heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ Relation r;
+
+ r = relation_openrv(relation, lockmode);
+
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+
+ return r;
}
/* ----------------
- * heap_endscan - end relation scan
+ * heap_openrv_extended - open a heap relation specified
+ * by a RangeVar node
*
- * See how to integrate with index scans.
- * Check handling if reldesc caching.
+ * As above, but optionally return NULL instead of failing for
+ * relation-not-found.
* ----------------
*/
-void
-heap_endscan(HeapScanDesc scan)
+Relation
+heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok)
{
- /* Note: no locking manipulations needed */
-
- /*
- * unpin scan buffers
- */
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
-
- /*
- * decrement relation reference count and free scan descriptor storage
- */
- RelationDecrementReferenceCount(scan->rs_rd);
-
- if (scan->rs_key)
- pfree(scan->rs_key);
+ Relation r;
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ r = relation_openrv_extended(relation, lockmode, missing_ok);
- if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ if (r)
+ {
+ if (r->rd_rel->relkind == RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is an index",
+ RelationGetRelationName(r))));
+ else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a composite type",
+ RelationGetRelationName(r))));
+ }
- pfree(scan);
+ return r;
}
/* ----------------
@@ -1567,383 +585,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
-/* ----------------
- * heap_parallelscan_startblock_init - find and set the scan's startblock
- *
- * Determine where the parallel seq scan should start. This function may
- * be called many times, once by each parallel worker. We must be careful
- * only to set the startblock once.
- * ----------------
- */
-static void
-heap_parallelscan_startblock_init(HeapScanDesc scan)
-{
- BlockNumber sync_startpage = InvalidBlockNumber;
- ParallelHeapScanDesc parallel_scan;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
-retry:
- /* Grab the spinlock. */
- SpinLockAcquire(¶llel_scan->phs_mutex);
-
- /*
- * If the scan's startblock has not yet been initialized, we must do so
- * now. If this is not a synchronized scan, we just start at block 0, but
- * if it is a synchronized scan, we must get the starting position from
- * the synchronized scan machinery. We can't hold the spinlock while
- * doing that, though, so release the spinlock, get the information we
- * need, and retry. If nobody else has initialized the scan in the
- * meantime, we'll fill in the value we fetched on the second time
- * through.
- */
- if (parallel_scan->phs_startblock == InvalidBlockNumber)
- {
- if (!parallel_scan->phs_syncscan)
- parallel_scan->phs_startblock = 0;
- else if (sync_startpage != InvalidBlockNumber)
- parallel_scan->phs_startblock = sync_startpage;
- else
- {
- SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
- goto retry;
- }
- }
- SpinLockRelease(¶llel_scan->phs_mutex);
-}
-
-/* ----------------
- * heap_parallelscan_nextpage - get the next page to scan
- *
- * Get the next page to scan. Even if there are no pages left to scan,
- * another backend could have grabbed a page to scan and not yet finished
- * looking at it, so it doesn't follow that the scan is done when the
- * first backend gets an InvalidBlockNumber return.
- * ----------------
- */
-static BlockNumber
-heap_parallelscan_nextpage(HeapScanDesc scan)
-{
- BlockNumber page;
- ParallelHeapScanDesc parallel_scan;
- uint64 nallocated;
-
- Assert(scan->rs_parallel);
- parallel_scan = scan->rs_parallel;
-
- /*
- * phs_nallocated tracks how many pages have been allocated to workers
- * already. When phs_nallocated >= rs_nblocks, all blocks have been
- * allocated.
- *
- * Because we use an atomic fetch-and-add to fetch the current value, the
- * phs_nallocated counter will exceed rs_nblocks, because workers will
- * still increment the value, when they try to allocate the next block but
- * all blocks have been allocated already. The counter must be 64 bits
- * wide because of that, to avoid wrapping around when rs_nblocks is close
- * to 2^32.
- *
- * The actual page to return is calculated by adding the counter to the
- * starting block number, modulo nblocks.
- */
- nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
- page = InvalidBlockNumber; /* all blocks have been allocated */
- else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
-
- /*
- * Report scan location. Normally, we report the current page number.
- * When we reach the end of the scan, though, we report the starting page,
- * not the ending page, just so the starting positions for later scans
- * doesn't slew backwards. We only report the position at the end of the
- * scan once, though: subsequent callers will report nothing.
- */
- if (scan->rs_syncscan)
- {
- if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
- }
-
- return page;
-}
-
-/* ----------------
- * heap_update_snapshot
- *
- * Update snapshot info in heap scan descriptor.
- * ----------------
- */
-void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
-{
- Assert(IsMVCCSnapshot(snapshot));
-
- RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
- scan->rs_temp_snap = true;
-}
-
-/* ----------------
- * heap_getnext - retrieve next tuple in scan
- *
- * Fix to work with index relations.
- * We don't return the buffer anymore, but you can get it from the
- * returned HeapTuple.
- * ----------------
- */
-
-#ifdef HEAPDEBUGALL
-#define HEAPDEBUG_1 \
- elog(DEBUG2, "heap_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
-#define HEAPDEBUG_2 \
- elog(DEBUG2, "heap_getnext returning EOS")
-#define HEAPDEBUG_3 \
- elog(DEBUG2, "heap_getnext returning tuple")
-#else
-#define HEAPDEBUG_1
-#define HEAPDEBUG_2
-#define HEAPDEBUG_3
-#endif /* !defined(HEAPDEBUGALL) */
-
-
-HeapTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
-{
- /* Note: no locking manipulations needed */
-
- HEAPDEBUG_1; /* heap_getnext( info ) */
-
- if (scan->rs_pageatatime)
- heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
- else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
-
- if (scan->rs_ctup.t_data == NULL)
- {
- HEAPDEBUG_2; /* heap_getnext returning EOS */
- return NULL;
- }
-
- /*
- * if we get here it means we have a new current scan tuple, so point to
- * the proper return buffer and return the tuple.
- */
- HEAPDEBUG_3; /* heap_getnext returning tuple */
-
- pgstat_count_heap_getnext(scan->rs_rd);
-
- return heap_copytuple(&(scan->rs_ctup));
-}
-
-/*
- * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
- *
- * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
- * of a HOT chain), and buffer is the buffer holding this tuple. We search
- * for the first chain member satisfying the given snapshot. If one is
- * found, we update *tid to reference that tuple's offset number, and
- * return TRUE. If no match, return FALSE without modifying *tid.
- *
- * heapTuple is a caller-supplied buffer. When a match is found, we return
- * the tuple here, in addition to updating *tid. If no match is found, the
- * contents of this buffer on return are undefined.
- *
- * If all_dead is not NULL, we check non-visible tuples to see if they are
- * globally dead; *all_dead is set TRUE if all members of the HOT chain
- * are vacuumable, FALSE if not.
- *
- * Unlike heap_fetch, the caller must already have pin and (at least) share
- * lock on the buffer; it is still pinned/locked at exit. Also unlike
- * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
- */
-bool
-heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
- Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call)
-{
- Page dp = (Page) BufferGetPage(buffer);
- TransactionId prev_xmax = InvalidTransactionId;
- OffsetNumber offnum;
- bool at_chain_start;
- bool valid;
- bool skip;
-
- /* If this is not the first call, previous call returned a (live!) tuple */
- if (all_dead)
- *all_dead = first_call;
-
- Assert(TransactionIdIsValid(RecentGlobalXmin));
-
- Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
- offnum = ItemPointerGetOffsetNumber(tid);
- at_chain_start = first_call;
- skip = !first_call;
-
- heapTuple->t_self = *tid;
-
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- heapTuple->t_tableOid = RelationGetRelid(relation);
- ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start.
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken.
- */
- if (TransactionIdIsValid(prev_xmax) &&
- !TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /*
- * When first_call is true (and thus, skip is initially false) we'll
- * return the first tuple we find. But on later passes, heapTuple
- * will initially be pointing to the tuple we returned last time.
- * Returning it again would be incorrect (and would loop forever), so
- * we skip it and return the next match we find.
- */
- if (!skip)
- {
- /*
- * For the benefit of logical decoding, have t_self point at the
- * element of the HOT chain we're currently investigating instead
- * of the root tuple of the HOT chain. This is important because
- * the *Satisfies routine for historical mvcc snapshots needs the
- * correct tid to decide about the visibility in some cases.
- */
- ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
-
- /* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, heapTuple,
- buffer, snapshot);
- /* reset to original, non-redirected, tid */
- heapTuple->t_self = *tid;
-
- if (valid)
- {
- ItemPointerSetOffsetNumber(tid, offnum);
- PredicateLockTuple(relation, heapTuple, snapshot);
- if (all_dead)
- *all_dead = false;
- return true;
- }
- }
- skip = false;
-
- /*
- * If we can't see it, maybe no one else can either. At caller
- * request, check whether all chain members are dead to all
- * transactions.
- *
- * Note: if you change the criterion here for what is "dead", fix the
- * planner's get_actual_variable_range() function to match.
- */
- if (all_dead && *all_dead &&
- !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
- *all_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so fetch
- * the next offnum and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
- at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- }
-
- return false;
-}
-
-/*
- * heap_hot_search - search HOT chain for tuple satisfying snapshot
- *
- * This has the same API as heap_hot_search_buffer, except that the caller
- * does not provide the buffer containing the page, rather we access it
- * locally.
- */
-bool
-heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
- bool *all_dead)
-{
- bool result;
- Buffer buffer;
- HeapTupleData heapTuple;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- result = heap_hot_search_buffer(tid, relation, buffer, snapshot,
- &heapTuple, all_dead, true);
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
- return result;
-}
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
*
@@ -4777,32 +3418,6 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
tuple->t_infomask2 = frz->t_infomask2;
}
-/*
- * heap_freeze_tuple
- * Freeze tuple in place, without WAL logging.
- *
- * Useful for callers like CLUSTER that perform their own WAL logging.
- */
-bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
-{
- xl_heap_freeze_tuple frz;
- bool do_freeze;
- bool tuple_totally_frozen;
-
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
- &frz, &tuple_totally_frozen);
-
- /*
- * Note that because this is not a WAL-logged operation, we don't need to
- * fill in the offset in the freeze record.
- */
-
- if (do_freeze)
- heap_execute_freeze_tuple(tuple, &frz);
- return do_freeze;
-}
/*
* For a given MultiXactId, return the hint bits that should be set in the
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 567e80de8c..4fa5ebc4e3 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1700,6 +1700,1111 @@ HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
}
}
+/* ----------------------------------------------------------------
+ * heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * heap_parallelscan_startblock_init - find and set the scan's startblock
+ *
+ * Determine where the parallel seq scan should start. This function may
+ * be called many times, once by each parallel worker. We must be careful
+ * only to set the startblock once.
+ * ----------------
+ */
+static void
+heap_parallelscan_startblock_init(HeapScanDesc scan)
+{
+ BlockNumber sync_startpage = InvalidBlockNumber;
+ ParallelHeapScanDesc parallel_scan;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+retry:
+ /* Grab the spinlock. */
+ SpinLockAcquire(¶llel_scan->phs_mutex);
+
+ /*
+ * If the scan's startblock has not yet been initialized, we must do so
+ * now. If this is not a synchronized scan, we just start at block 0, but
+ * if it is a synchronized scan, we must get the starting position from
+ * the synchronized scan machinery. We can't hold the spinlock while
+ * doing that, though, so release the spinlock, get the information we
+ * need, and retry. If nobody else has initialized the scan in the
+ * meantime, we'll fill in the value we fetched on the second time
+ * through.
+ */
+ if (parallel_scan->phs_startblock == InvalidBlockNumber)
+ {
+ if (!parallel_scan->phs_syncscan)
+ parallel_scan->phs_startblock = 0;
+ else if (sync_startpage != InvalidBlockNumber)
+ parallel_scan->phs_startblock = sync_startpage;
+ else
+ {
+ SpinLockRelease(¶llel_scan->phs_mutex);
+ sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ goto retry;
+ }
+ }
+ SpinLockRelease(¶llel_scan->phs_mutex);
+}
+
+/* ----------------
+ * heap_parallelscan_nextpage - get the next page to scan
+ *
+ * Get the next page to scan. Even if there are no pages left to scan,
+ * another backend could have grabbed a page to scan and not yet finished
+ * looking at it, so it doesn't follow that the scan is done when the
+ * first backend gets an InvalidBlockNumber return.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(HeapScanDesc scan)
+{
+ BlockNumber page;
+ ParallelHeapScanDesc parallel_scan;
+ uint64 nallocated;
+
+ Assert(scan->rs_parallel);
+ parallel_scan = scan->rs_parallel;
+
+ /*
+ * phs_nallocated tracks how many pages have been allocated to workers
+ * already. When phs_nallocated >= rs_nblocks, all blocks have been
+ * allocated.
+ *
+ * Because we use an atomic fetch-and-add to fetch the current value, the
+ * phs_nallocated counter will exceed rs_nblocks, because workers will
+ * still increment the value, when they try to allocate the next block but
+ * all blocks have been allocated already. The counter must be 64 bits
+ * wide because of that, to avoid wrapping around when rs_nblocks is close
+ * to 2^32.
+ *
+ * The actual page to return is calculated by adding the counter to the
+ * starting block number, modulo nblocks.
+ */
+ nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
+ if (nallocated >= scan->rs_nblocks)
+ page = InvalidBlockNumber; /* all blocks have been allocated */
+ else
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+
+ /*
+ * Report scan location. Normally, we report the current page number.
+ * When we reach the end of the scan, though, we report the starting page,
+ * not the ending page, just so the starting positions for later scans
+ * doesn't slew backwards. We only report the position at the end of the
+ * scan once, though: subsequent callers will report nothing.
+ */
+ if (scan->rs_syncscan)
+ {
+ if (page != InvalidBlockNumber)
+ ss_report_location(scan->rs_rd, page);
+ else if (nallocated == scan->rs_nblocks)
+ ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ }
+
+ return page;
+}
+
+
+/* ----------------
+ * initscan - scan code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
+{
+ bool allow_strat;
+ bool allow_sync;
+
+ /*
+ * Determine the number of blocks we have to scan.
+ *
+ * It is sufficient to do this once at scan start, since any tuples added
+ * while the scan is in progress will be invisible to my snapshot anyway.
+ * (That is not true when using a non-MVCC snapshot. However, we couldn't
+ * guarantee to return tuples added after scan start anyway, since they
+ * might go into pages we already scanned. To guarantee consistent
+ * results for a non-MVCC snapshot, the caller must hold some higher-level
+ * lock that ensures the interesting tuple(s) won't change.)
+ */
+ if (scan->rs_parallel != NULL)
+ scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ else
+ scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+
+ /*
+ * If the table is large relative to NBuffers, use a bulk-read access
+ * strategy and enable synchronized scanning (see syncscan.c). Although
+ * the thresholds for these features could be different, we make them the
+ * same so that there are only two behaviors to tune rather than four.
+ * (However, some callers need to be able to disable one or both of these
+ * behaviors, independently of the size of the table; also there is a GUC
+ * variable that can disable synchronized scanning.)
+ *
+ * Note that heap_parallelscan_initialize has a very similar test; if you
+ * change this, consider changing that one, too.
+ */
+ if (!RelationUsesLocalBuffers(scan->rs_rd) &&
+ scan->rs_nblocks > NBuffers / 4)
+ {
+ allow_strat = scan->rs_allow_strat;
+ allow_sync = scan->rs_allow_sync;
+ }
+ else
+ allow_strat = allow_sync = false;
+
+ if (allow_strat)
+ {
+ /* During a rescan, keep the previous strategy object. */
+ if (scan->rs_strategy == NULL)
+ scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ }
+ else
+ {
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+ scan->rs_strategy = NULL;
+ }
+
+ if (scan->rs_parallel != NULL)
+ {
+ /* For parallel scan, believe whatever ParallelHeapScanDesc says. */
+ scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ }
+ else if (keep_startblock)
+ {
+ /*
+ * When rescanning, we want to keep the previous startblock setting,
+ * so that rewinding a cursor doesn't generate surprising results.
+ * Reset the active syncscan setting, though.
+ */
+ scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ }
+ else if (allow_sync && synchronize_seqscans)
+ {
+ scan->rs_syncscan = true;
+ scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ }
+ else
+ {
+ scan->rs_syncscan = false;
+ scan->rs_startblock = 0;
+ }
+
+ scan->rs_numblocks = InvalidBlockNumber;
+ scan->rs_inited = false;
+ scan->rs_ctup.t_data = NULL;
+ ItemPointerSetInvalid(&scan->rs_ctup.t_self);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+
+ /* page-at-a-time fields are always invalid when not rs_inited */
+
+ /*
+ * copy the scan key, if appropriate
+ */
+ if (key != NULL)
+ memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+
+ /*
+ * Currently, we don't have a stats counter for bitmap heap scans (but the
+ * underlying bitmap index scans will be counted) or sample scans (we only
+ * update stats for tuple fetches there)
+ */
+ if (!scan->rs_bitmapscan && !scan->rs_samplescan)
+ pgstat_count_heap_scan(scan->rs_rd);
+}
+
+
+/* ----------------
+ * heapgettup - fetch next heap tuple
+ *
+ * Initialize the scan if not already done; then advance to the next
+ * tuple as indicated by "dir"; return the next tuple in scan->rs_ctup,
+ * or set scan->rs_ctup.t_data = NULL if no more tuples.
+ *
+ * dir == NoMovementScanDirection means "re-fetch the tuple indicated
+ * by scan->rs_ctup".
+ *
+ * Note: the reason nkeys/key are passed separately, even though they are
+ * kept in the scan descriptor, is that the caller may not want us to check
+ * the scankeys.
+ *
+ * Note: when we fall off the end of the scan in either direction, we
+ * reset rs_inited. This means that a further request with the same
+ * scan direction will restart the scan, which is a bit odd, but a
+ * request with the opposite scan direction will start a fresh scan
+ * in the proper direction. The latter is required behavior for cursors,
+ * while the former case is generally undefined behavior in Postgres
+ * so we don't care too much.
+ * ----------------
+ */
+static void
+heapgettup(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ Snapshot snapshot = scan->rs_snapshot;
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineoff, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineoff = FirstOffsetNumber; /* first offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineoff = /* next offnum */
+ OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+ /* page and lineoff now reference the physically next tid */
+
+ linesleft = lines - lineoff + 1;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber(dp);
+
+ if (!scan->rs_inited)
+ {
+ lineoff = lines; /* final offnum */
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineoff = /* previous offnum */
+ OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
+ }
+ /* page and lineoff now reference the physically previous tid */
+
+ linesleft = lineoff;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ lpp = PageGetItemId(dp, lineoff);
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ if (ItemIdIsNormal(lpp))
+ {
+ bool valid;
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
+ snapshot,
+ scan->rs_cbuf);
+
+ CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
+ scan->rs_cbuf, snapshot);
+
+ if (valid && key != NULL)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+
+ if (valid)
+ {
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ {
+ --lpp; /* move back in this page's ItemId array */
+ --lineoff;
+ }
+ else
+ {
+ ++lpp; /* move forward in this page's ItemId array */
+ ++lineoff;
+ }
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * advance to next/prior page and detect end of scan
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ lines = PageGetMaxOffsetNumber((Page) dp);
+ linesleft = lines;
+ if (backward)
+ {
+ lineoff = lines;
+ lpp = PageGetItemId(dp, lines);
+ }
+ else
+ {
+ lineoff = FirstOffsetNumber;
+ lpp = PageGetItemId(dp, FirstOffsetNumber);
+ }
+ }
+}
+
+/* ----------------
+ * heapgettup_pagemode - fetch next heap tuple in page-at-a-time mode
+ *
+ * Same API as heapgettup, but used in page-at-a-time mode
+ *
+ * The internal logic is much the same as heapgettup's too, but there are some
+ * differences: we do not take the buffer content lock (that only needs to
+ * happen inside heapgetpage), and we iterate through just the tuples listed
+ * in rs_vistuples[] rather than all tuples on the page. Notice that
+ * lineindex is 0-based, where the corresponding loop variable lineoff in
+ * heapgettup is 1-based.
+ * ----------------
+ */
+static void
+heapgettup_pagemode(HeapScanDesc scan,
+ ScanDirection dir,
+ int nkeys,
+ ScanKey key)
+{
+ HeapTuple tuple = &(scan->rs_ctup);
+ bool backward = ScanDirectionIsBackward(dir);
+ BlockNumber page;
+ bool finished;
+ Page dp;
+ int lines;
+ int lineindex;
+ OffsetNumber lineoff;
+ int linesleft;
+ ItemId lpp;
+
+ /*
+ * calculate next starting lineindex, given scan direction
+ */
+ if (ScanDirectionIsForward(dir))
+ {
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ if (scan->rs_parallel != NULL)
+ {
+ heap_parallelscan_startblock_init(scan);
+
+ page = heap_parallelscan_nextpage(scan);
+
+ /* Other processes might have already finished the scan. */
+ if (page == InvalidBlockNumber)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+ }
+ else
+ page = scan->rs_startblock; /* first page */
+ heapgetpage(scan, page);
+ lineindex = 0;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ lineindex = scan->rs_cindex + 1;
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ /* page and lineindex now reference the next visible tid */
+
+ linesleft = lines - lineindex;
+ }
+ else if (backward)
+ {
+ /* backward parallel scan not supported */
+ Assert(scan->rs_parallel == NULL);
+
+ if (!scan->rs_inited)
+ {
+ /*
+ * return null immediately if relation is empty
+ */
+ if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ /*
+ * Disable reporting to syncscan logic in a backwards scan; it's
+ * not very likely anyone else is doing the same thing at the same
+ * time, and much more likely that we'll just bollix things for
+ * forward scanners.
+ */
+ scan->rs_syncscan = false;
+ /* start from last page of the scan */
+ if (scan->rs_startblock > 0)
+ page = scan->rs_startblock - 1;
+ else
+ page = scan->rs_nblocks - 1;
+ heapgetpage(scan, page);
+ }
+ else
+ {
+ /* continue from previously returned page/tuple */
+ page = scan->rs_cblock; /* current page */
+ }
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+
+ if (!scan->rs_inited)
+ {
+ lineindex = lines - 1;
+ scan->rs_inited = true;
+ }
+ else
+ {
+ lineindex = scan->rs_cindex - 1;
+ }
+ /* page and lineindex now reference the previous visible tid */
+
+ linesleft = lineindex + 1;
+ }
+ else
+ {
+ /*
+ * ``no movement'' scan direction: refetch prior tuple
+ */
+ if (!scan->rs_inited)
+ {
+ Assert(!BufferIsValid(scan->rs_cbuf));
+ tuple->t_data = NULL;
+ return;
+ }
+
+ page = ItemPointerGetBlockNumber(&(tuple->t_self));
+ if (page != scan->rs_cblock)
+ heapgetpage(scan, page);
+
+ /* Since the tuple was previously fetched, needn't lock page here */
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+
+ /* check that rs_cindex is in sync */
+ Assert(scan->rs_cindex < scan->rs_ntuples);
+ Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+
+ return;
+ }
+
+ /*
+ * advance the scan until we find a qualifying tuple or run out of stuff
+ * to scan
+ */
+ for (;;)
+ {
+ while (linesleft > 0)
+ {
+ lineoff = scan->rs_vistuples[lineindex];
+ lpp = PageGetItemId(dp, lineoff);
+ Assert(ItemIdIsNormal(lpp));
+
+ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
+ tuple->t_len = ItemIdGetLength(lpp);
+ ItemPointerSet(&(tuple->t_self), page, lineoff);
+
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
+ {
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
+
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+ else
+ {
+ scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+
+ /*
+ * otherwise move to the next item on the page
+ */
+ --linesleft;
+ if (backward)
+ --lineindex;
+ else
+ ++lineindex;
+ }
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ if (backward)
+ {
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ if (page == 0)
+ page = scan->rs_nblocks;
+ page--;
+ }
+ else if (scan->rs_parallel != NULL)
+ {
+ page = heap_parallelscan_nextpage(scan);
+ finished = (page == InvalidBlockNumber);
+ }
+ else
+ {
+ page++;
+ if (page >= scan->rs_nblocks)
+ page = 0;
+ finished = (page == scan->rs_startblock) ||
+ (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+
+ /*
+ * Report our new scan position for synchronization purposes. We
+ * don't do that when moving backwards, however. That would just
+ * mess up any other forward-moving scanners.
+ *
+ * Note: we do this before checking for end of scan so that the
+ * final state of the position hint is back at the start of the
+ * rel. That's not strictly necessary, but otherwise when you run
+ * the same query multiple times the starting position would shift
+ * a little bit backwards on every invocation, which is confusing.
+ * We don't guarantee any specific ordering in general, though.
+ */
+ if (scan->rs_syncscan)
+ ss_report_location(scan->rs_rd, page);
+ }
+
+ /*
+ * return NULL if we've exhausted all the pages
+ */
+ if (finished)
+ {
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+ scan->rs_cbuf = InvalidBuffer;
+ scan->rs_cblock = InvalidBlockNumber;
+ tuple->t_data = NULL;
+ scan->rs_inited = false;
+ return;
+ }
+
+ heapgetpage(scan, page);
+
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_cbuf);
+ TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ lines = scan->rs_ntuples;
+ linesleft = lines;
+ if (backward)
+ lineindex = lines - 1;
+ else
+ lineindex = 0;
+ }
+}
+
+
+static HeapScanDesc
+heapam_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
+{
+ HeapScanDesc scan;
+
+ /*
+ * increment relation ref count while scanning relation
+ *
+ * This is just to make really sure the relcache entry won't go away while
+ * the scan has a pointer to it. Caller should be holding the rel open
+ * anyway, so this is redundant in all normal scenarios...
+ */
+ RelationIncrementReferenceCount(relation);
+
+ /*
+ * allocate and initialize scan descriptor
+ */
+ scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+
+ scan->rs_rd = relation;
+ scan->rs_snapshot = snapshot;
+ scan->rs_nkeys = nkeys;
+ scan->rs_bitmapscan = is_bitmapscan;
+ scan->rs_samplescan = is_samplescan;
+ scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
+ scan->rs_parallel = parallel_scan;
+
+ /*
+ * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+ */
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+
+ /*
+ * For a seqscan in a serializable transaction, acquire a predicate lock
+ * on the entire relation. This is required not only to lock all the
+ * matching tuples, but also to conflict with new insertions into the
+ * table. In an indexscan, we take page locks on the index pages covering
+ * the range specified in the scan qual, but in a heap scan there is
+ * nothing more fine-grained to lock. A bitmap scan is a different story,
+ * there we have already scanned the index and locked the index pages
+ * covering the predicate. But in that case we still have to lock any
+ * matching heap tuples.
+ */
+ if (!is_bitmapscan)
+ PredicateLockRelation(relation, snapshot);
+
+ /* we only need to set this up once */
+ scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * we do this here instead of in initscan() because heap_rescan also calls
+ * initscan() and we don't want to allocate memory again
+ */
+ if (nkeys > 0)
+ scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ else
+ scan->rs_key = NULL;
+
+ initscan(scan, key, false);
+
+ return scan;
+}
+
+/* ----------------
+ * heapam_rescan - restart a relation scan
+ * ----------------
+ */
+static void
+heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * reinitialize scan descriptor
+ */
+ initscan(scan, key, true);
+
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
+}
+
+/* ----------------
+ * heapam_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+static void
+heapam_endscan(HeapScanDesc scan)
+{
+ /* Note: no locking manipulations needed */
+
+ /*
+ * unpin scan buffers
+ */
+ if (BufferIsValid(scan->rs_cbuf))
+ ReleaseBuffer(scan->rs_cbuf);
+
+ /*
+ * decrement relation reference count and free scan descriptor storage
+ */
+ RelationDecrementReferenceCount(scan->rs_rd);
+
+ if (scan->rs_key)
+ pfree(scan->rs_key);
+
+ if (scan->rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_strategy);
+
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
+ pfree(scan);
+}
+
+/* ----------------
+ * heapam_scan_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+static void
+heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ Assert(IsMVCCSnapshot(snapshot));
+
+ RegisterSnapshot(snapshot);
+ scan->rs_snapshot = snapshot;
+ scan->rs_temp_snap = true;
+}
+
+/* ----------------
+ * heapam_getnext - retrieve next tuple in scan
+ *
+ * Fix to work with index relations.
+ * We don't return the buffer anymore, but you can get it from the
+ * returned HeapTuple.
+ * ----------------
+ */
+
+#ifdef HEAPAMDEBUGALL
+#define HEAPAMDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMDEBUG_1
+#define HEAPAMDEBUG_2
+#define HEAPAMDEBUG_3
+#endif /* !defined(HEAPDEBUGALL) */
+
+
+static StorageTuple
+heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMDEBUG_2; /* heap_getnext returning EOS */
+ return NULL;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+static TupleTableSlot *
+heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
/*
* heap_fetch - retrieve tuple with given tid
@@ -1852,9 +2957,213 @@ heap_fetch(Relation relation,
return false;
}
+/*
+ * heapam_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+static bool
+heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ Page dp = (Page) BufferGetPage(buffer);
+ TransactionId prev_xmax = InvalidTransactionId;
+ OffsetNumber offnum;
+ bool at_chain_start;
+ bool valid;
+ bool skip;
+
+ /* If this is not the first call, previous call returned a (live!) tuple */
+ if (all_dead)
+ *all_dead = first_call;
+
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+
+ Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
+ offnum = ItemPointerGetOffsetNumber(tid);
+ at_chain_start = first_call;
+ skip = !first_call;
+
+ heapTuple->t_self = *tid;
+
+ /* Scan through possible multiple members of HOT-chain */
+ for (;;)
+ {
+ ItemId lp;
+
+ /* check for bogus TID */
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
+ break;
+
+ lp = PageGetItemId(dp, offnum);
+
+ /* check for unused, dead, or redirected items */
+ if (!ItemIdIsNormal(lp))
+ {
+ /* We should only see a redirect at start of chain */
+ if (ItemIdIsRedirected(lp) && at_chain_start)
+ {
+ /* Follow the redirect */
+ offnum = ItemIdGetRedirect(lp);
+ at_chain_start = false;
+ continue;
+ }
+ /* else must be end of chain */
+ break;
+ }
+
+ heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
+ heapTuple->t_len = ItemIdGetLength(lp);
+ heapTuple->t_tableOid = RelationGetRelid(relation);
+ ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum);
+
+ /*
+ * Shouldn't see a HEAP_ONLY tuple at chain start.
+ */
+ if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
+ break;
+
+ /*
+ * The xmin should match the previous xmax value, else chain is
+ * broken.
+ */
+ if (TransactionIdIsValid(prev_xmax) &&
+ !TransactionIdEquals(prev_xmax,
+ HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ break;
+
+ /*
+ * When first_call is true (and thus, skip is initially false) we'll
+ * return the first tuple we find. But on later passes, heapTuple
+ * will initially be pointing to the tuple we returned last time.
+ * Returning it again would be incorrect (and would loop forever), so
+ * we skip it and return the next match we find.
+ */
+ if (!skip)
+ {
+ /*
+ * For the benefit of logical decoding, have t_self point at the
+ * element of the HOT chain we're currently investigating instead
+ * of the root tuple of the HOT chain. This is important because
+ * the *Satisfies routine for historical mvcc snapshots needs the
+ * correct tid to decide about the visibility in some cases.
+ */
+ ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
+
+ /* If it's visible per the snapshot, we must return it */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, heapTuple,
+ buffer, snapshot);
+ /* reset to original, non-redirected, tid */
+ heapTuple->t_self = *tid;
+
+ if (valid)
+ {
+ ItemPointerSetOffsetNumber(tid, offnum);
+ PredicateLockTuple(relation, heapTuple, snapshot);
+ if (all_dead)
+ *all_dead = false;
+ return true;
+ }
+ }
+ skip = false;
+
+ /*
+ * If we can't see it, maybe no one else can either. At caller
+ * request, check whether all chain members are dead to all
+ * transactions.
+ *
+ * Note: if you change the criterion here for what is "dead", fix the
+ * planner's get_actual_variable_range() function to match.
+ */
+ if (all_dead && *all_dead &&
+ !HeapTupleIsSurelyDead(heapTuple, RecentGlobalXmin))
+ *all_dead = false;
+
+ /*
+ * Check to see if HOT chain continues past this tuple; if so fetch
+ * the next offnum and loop around.
+ */
+ if (HeapTupleIsHotUpdated(heapTuple))
+ {
+ Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
+ ItemPointerGetBlockNumber(tid));
+ offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
+ at_chain_start = false;
+ prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ }
+ else
+ break; /* end of chain */
+ }
+
+ return false;
+}
+
+/*
+ * heapam_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+static void
+heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_inited); /* else too late to change */
+ Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+
+ /* Check startBlk is valid (but allow case of zero blocks...) */
+ Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+
+ scan->rs_startblock = startBlk;
+ scan->rs_numblocks = numBlks;
+}
+
+
+/*
+ * heapam_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+static bool
+heapam_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ xl_heap_freeze_tuple frz;
+ bool do_freeze;
+ bool tuple_totally_frozen;
+ do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ &frz, &tuple_totally_frozen);
+ /*
+ * Note that because this is not a WAL-logged operation, we don't need to
+ * fill in the offset in the freeze record.
+ */
+ if (do_freeze)
+ heap_execute_freeze_tuple(tuple, &frz);
+ return do_freeze;
+}
/* ----------------------------------------------------------------
* storage AM support routines for heapam
@@ -3875,6 +5184,16 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->scan_begin = heapam_beginscan;
+ amroutine->scansetlimits = heapam_setscanlimits;
+ amroutine->scan_getnext = heapam_getnext;
+ amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_end = heapam_endscan;
+ amroutine->scan_rescan = heapam_rescan;
+ amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
+ amroutine->tuple_freeze = heapam_freeze_tuple;
+ amroutine->hot_search_buffer = heapam_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 84ac4bfade..4691e44ecc 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -409,8 +409,8 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
- state->rs_cutoff_multi);
+ storage_freeze_tuple(state->rs_new_rel, new_tuple->t_data, state->rs_freeze_xid,
+ state->rs_cutoff_multi);
/*
* Invalid ctid means that ctid should point to the tuple itself. We'll
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 870490d00f..0b83ecf1bc 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -48,6 +48,174 @@
#include "utils/tqual.h"
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to TRUE with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot *
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
@@ -99,6 +267,73 @@ storage_fetch(Relation relation,
userbuf, keep_buf, stats_relation);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return TRUE. If no match, return FALSE without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set TRUE if all members of the HOT chain
+ * are vacuumable, FALSE if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
+
+/*
+ * heap_freeze_tuple
+ * Freeze tuple in place, without WAL logging.
+ *
+ * Useful for callers like CLUSTER that perform their own WAL logging.
+ */
+bool
+storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi)
+{
+ return rel->rd_stamroutine->tuple_freeze(tuple, cutoff_xid, cutoff_multi);
+}
+
/*
* storage_lock_tuple - lock a tuple in shared or exclusive mode
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2543..26a9ccb657 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,9 +395,9 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
- nkeys, key,
- true, false);
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
+ nkeys, key,
+ true, false);
sysscan->iscan = NULL;
}
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bef4255369..407e381a91 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,12 +606,12 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
- scan->xs_cbuf,
- scan->xs_snapshot,
- &scan->xs_ctup,
- &all_dead,
- !scan->xs_continue_hot);
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
+ scan->xs_cbuf,
+ scan->xs_snapshot,
+ &scan->xs_ctup,
+ &all_dead,
+ !scan->xs_continue_hot);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
if (got_heap_tuple)
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 5cbaba1b7d..309223ec4b 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,8 +326,8 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
- &all_dead))
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
+ &all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..a73a363a49 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "bootstrap/bootstrap.h"
@@ -586,18 +587,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -605,7 +606,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -916,25 +917,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ccde66a7dd..d2a8a06097 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -797,14 +798,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
+ scan = storage_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -842,14 +843,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 17be9a4042..a201b00cae 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1904,10 +1905,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2279,16 +2280,16 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- allow_sync); /* syncscan OK? */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ allow_sync); /* syncscan OK? */
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2301,7 +2302,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2613,7 +2614,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2684,14 +2685,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- true); /* syncscan OK */
-
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ true); /* syncscan OK */
+
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2727,7 +2728,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3004,17 +3005,17 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- false); /* syncscan not OK */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ false); /* syncscan not OK */
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3171,7 +3172,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 5daa8a1c19..a5a5268d12 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -709,11 +710,11 @@ extern PartitionBoundInfo
partition_bounds_copy(PartitionBoundInfo src,
PartitionKey key)
{
- PartitionBoundInfo dest;
- int i;
- int ndatums;
- int partnatts;
- int num_indexes;
+ PartitionBoundInfo dest;
+ int i;
+ int ndatums;
+ int partnatts;
+ int num_indexes;
dest = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
@@ -732,11 +733,11 @@ partition_bounds_copy(PartitionBoundInfo src,
if (src->kind != NULL)
{
dest->kind = (PartitionRangeDatumKind **) palloc(ndatums *
- sizeof(PartitionRangeDatumKind *));
+ sizeof(PartitionRangeDatumKind *));
for (i = 0; i < ndatums; i++)
{
dest->kind[i] = (PartitionRangeDatumKind *) palloc(partnatts *
- sizeof(PartitionRangeDatumKind));
+ sizeof(PartitionRangeDatumKind));
memcpy(dest->kind[i], src->kind[i],
sizeof(PartitionRangeDatumKind) * key->partnatts);
@@ -747,7 +748,8 @@ partition_bounds_copy(PartitionBoundInfo src,
for (i = 0; i < ndatums; i++)
{
- int j;
+ int j;
+
dest->datums[i] = (Datum *) palloc(sizeof(Datum) * partnatts);
for (j = 0; j < partnatts; j++)
@@ -1074,7 +1076,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -1083,7 +1085,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1099,7 +1101,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc349a..1d048e6394 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471bc83..517e3101cd 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8cd5..145e3c1d65 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71cd6..a51f2e4dfc 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc172..e0f6973a3f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e51ed..6727d154e1 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index ad28b3af3e..cad0eed1a4 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2036,10 +2036,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2051,7 +2051,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e138539035..39850b1b37 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3f615b6260..5209083a72 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1947,8 +1948,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -1988,7 +1989,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 458c897741..3bab05079e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4551,7 +4551,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4559,7 +4559,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4673,7 +4673,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5076,9 +5076,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5090,7 +5090,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8257,7 +8257,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8265,7 +8265,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8280,7 +8280,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8335,9 +8335,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8366,7 +8366,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10871,8 +10871,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10931,7 +10931,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8559c3b6b3..cdfa8ffb3f 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 680a65cad5..a0f3db5bdb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,8 +15,9 @@
#include "access/genam.h"
#include "access/heapam.h"
-#include "access/sysattr.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7df942b18b..b95e3fc5ab 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2387,8 +2388,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2417,7 +2418,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2783,8 +2784,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2827,7 +2828,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index cbd6e9b161..bafc15eac6 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -68,7 +69,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
-static List *expand_vacuum_rel(VacuumRelation *vrel);
+static List *expand_vacuum_rel(VacuumRelation * vrel);
static List *get_all_vacuum_rels(void);
static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
@@ -423,7 +424,7 @@ vacuum(int options, List *relations, VacuumParams *params,
* are made in vac_context.
*/
static List *
-expand_vacuum_rel(VacuumRelation *vrel)
+expand_vacuum_rel(VacuumRelation * vrel)
{
List *vacrels = NIL;
MemoryContext oldcontext;
@@ -533,9 +534,9 @@ get_all_vacuum_rels(void)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
MemoryContext oldcontext;
@@ -562,7 +563,7 @@ get_all_vacuum_rels(void)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
return vacrels;
@@ -1213,9 +1214,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1252,7 +1253,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5b88..dde843174e 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index ab533cf9c7..e852718100 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 24b5ff7298..ed2c0ab80c 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,19 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start an index scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +312,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index d618b3d903..742f73176c 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -683,7 +683,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -767,7 +767,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1087,7 +1087,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1095,7 +1095,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 98eb777421..8ce51e5bd5 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -597,7 +597,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 2b118359b5..d8c4119972 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3119,7 +3119,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3244,7 +3244,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 3e22c6baf6..a661323fda 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h"
@@ -433,8 +434,8 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
- &heapTuple, NULL, true))
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ &heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
@@ -747,7 +748,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
/* release bitmaps and buffers if any */
if (node->tbmiterator)
@@ -837,7 +838,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -952,10 +953,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
- estate->es_snapshot,
- 0,
- NULL);
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
+ estate->es_snapshot,
+ 0,
+ NULL);
/*
* get the scan type from the relation descriptor.
@@ -1111,5 +1112,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node, shm_toc *toc)
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 02f6c816aa..d3de6c5824 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 639f4f5af8..edb8046180 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -45,7 +45,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -251,7 +251,7 @@ gather_getnext(GatherState *gatherstate)
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
MemoryContext tupleContext = gatherstate->ps.ps_ExprContext->ecxt_per_tuple_memory;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -294,7 +294,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -302,7 +302,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 5625b12521..3381813455 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,7 +44,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -53,8 +53,8 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
- bool nowait, bool *done);
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+ bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
static void gather_merge_init(GatherMergeState *gm_state);
@@ -399,7 +399,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -617,7 +617,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -689,12 +689,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
MemoryContext oldContext;
MemoryContext tupleContext;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 9368ca04f8..07346435cd 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,9 +186,9 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
- * index AM might fill both fields, in which case we prefer the heap
- * format, since it's probably a bit cheaper to fill a slot from.
+ * provided in either StorageTuple or IndexTuple format. Conceivably
+ * an index AM might fill both fields, in which case we prefer the
+ * heap format, since it's probably a bit cheaper to fill a slot from.
*/
if (scandesc->xs_hitup)
{
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 262008240d..2726933d9d 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1883..64f3dd8be0 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,10 +29,12 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
HeapScanDesc scan);
+//hari
+
/* ----------------------------------------------------------------
* Scan Support
* ----------------------------------------------------------------
@@ -47,7 +49,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -244,7 +246,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,19 +351,19 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
- scanstate->ss.ps.state->es_snapshot,
- 0, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ scanstate->ss.ps.state->es_snapshot,
+ 0, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
pfree(params);
@@ -376,7 +378,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +556,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 76bec780a8..0521df1fa9 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,35 +69,16 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
- estate->es_snapshot,
- 0, NULL);
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
+ estate->es_snapshot,
+ 0, NULL);
node->ss.ss_currentScanDesc = scandesc;
}
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,13 +251,13 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
- NULL); /* new scan keys */
+ storage_rescan(scan, /* scan desc */
+ NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
}
@@ -323,7 +304,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -354,5 +335,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 02868749f6..a68584378b 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2092,7 +2092,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2175,7 +2175,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 40292b86c1..557cb2175c 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -633,11 +633,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -661,7 +661,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -692,7 +692,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -860,7 +860,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -968,7 +968,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1689,7 +1689,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1720,8 +1720,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 9a47276274..3d61d96775 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -166,7 +166,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
HeapTupleData htup;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb01b..0d2e1733bf 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1883,9 +1883,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1912,7 +1912,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2043,13 +2043,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2135,7 +2135,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2143,8 +2143,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2190,7 +2190,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 3a0b49c7c4..36808c4067 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 2b2b993e2c..88086f3d48 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4268,8 +4268,8 @@ BackendInitialize(Port *port)
*
* postgres: walsender <user> <host> <activity>
*
- * To achieve that, we pass "walsender" as username and username
- * as dbname to init_ps_display(). XXX: should add a new variant of
+ * To achieve that, we pass "walsender" as username and username as dbname
+ * to init_ps_display(). XXX: should add a new variant of
* init_ps_display() to avoid abusing the parameters like this.
*/
if (am_walsender)
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index a613ef4757..83ec2dfcbe 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 071b3a9ec9..4924daca76 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 20f1d279e9..03b7cc76d7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/session.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1212,10 +1213,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index cdd45ef313..4cddd73355 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -106,40 +106,16 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
- BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
-extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
-extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
- Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
- bool *all_dead, bool first_call);
-extern bool heap_hot_search(ItemPointer tid, Relation relation,
- Snapshot snapshot, bool *all_dead);
+
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
@@ -147,8 +123,6 @@ extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi);
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -158,8 +132,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
-
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index 14aad60546..597f2bd2f4 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -107,6 +107,9 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
/* Get the LOCKMODE for a given MultiXactStatus */
#define LOCKMODE_from_mxstatus(status) \
(tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+
+extern bool synchronize_seqscans;
+
extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
@@ -136,6 +139,11 @@ extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+
+extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+ MultiXactId cutoff_multi, Buffer buf);
+extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
+
extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
LockTupleMode mode, LockWaitPolicy wait_policy,
bool *have_tuple_lock);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index 447935ba10..ec5dc89a26 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -20,6 +20,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
typedef union tuple_data
{
@@ -42,6 +43,34 @@ typedef enum tuple_data_flags
typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
bool *specConflict, List *arbiterIndexes);
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -50,6 +79,16 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
+extern bool storage_freeze_tuple(Relation rel, HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d8947a3..7f9bef1374 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e92e..2781975530 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,10 +117,10 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
- int *attnum, Datum *Values, const char *Nulls);
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+ int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index fdc9deb2b2..9905ecd8ba 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -26,7 +26,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
/* Use these to receive tuples from a shm_mq. */
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
- bool nowait, bool *done);
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
+ bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 223eef28d1..54b37f7db5 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -237,7 +237,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.14.2.windows.1
[application/octet-stream] 0008-Remove-HeapScanDesc-usage-outside-heap.patch (93.6K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/4-0008-Remove-HeapScanDesc-usage-outside-heap.patch)
download | inline diff:
From 5028a8b93e74cc28b5e545ff60e33b1b0da5d0f7 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 1 Nov 2017 14:11:58 +1100
Subject: [PATCH 8/8] Remove HeapScanDesc usage outside heap
HeapScanDesc is divided into two scan descriptors.
StorageScanDesc and HeapPageScanDesc.
StorageScanDesc has common members that are should
be available across all the storage routines and
HeapPageScanDesc is avaiable only for the storage
routine that supports Heap storage with page format.
The HeapPageScanDesc is used internally by the heapam
storage routine and also this is exposed to Bitmap Heap
and Sample scan's as they depend on the Heap page format.
while generating the Bitmap Heap and Sample scan's,
the planner now checks whether the storage routine
supports returning HeapPageScanDesc or not? Based on
this decision, the planner plans above two plans.
---
contrib/pgrowlocks/pgrowlocks.c | 4 +-
contrib/pgstattuple/pgstattuple.c | 10 +-
contrib/tsm_system_rows/tsm_system_rows.c | 18 +-
contrib/tsm_system_time/tsm_system_time.c | 8 +-
src/backend/access/heap/heapam.c | 37 +--
src/backend/access/heap/heapam_storage.c | 436 ++++++++++++++++-------------
src/backend/access/heap/storageam.c | 51 +++-
src/backend/access/index/genam.c | 4 +-
src/backend/access/tablesample/system.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 4 +-
src/backend/catalog/index.c | 8 +-
src/backend/catalog/partition.c | 2 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/cluster.c | 4 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 10 +-
src/backend/commands/tablespace.c | 10 +-
src/backend/commands/typecmds.c | 4 +-
src/backend/commands/vacuum.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 69 ++---
src/backend/executor/nodeSamplescan.c | 50 ++--
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/optimizer/util/plancat.c | 10 +-
src/backend/postmaster/autovacuum.c | 4 +-
src/backend/postmaster/pgstat.c | 2 +-
src/backend/replication/logical/launcher.c | 2 +-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/utils/init/postinit.c | 2 +-
src/include/access/heapam.h | 4 +-
src/include/access/relscan.h | 47 ++--
src/include/access/storageam.h | 44 +--
src/include/access/storageamapi.h | 42 +--
src/include/nodes/execnodes.h | 4 +-
39 files changed, 519 insertions(+), 406 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index bc8b423975..762d969ecd 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -56,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -71,7 +71,7 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index c4b10d6efc..32ac121e92 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -328,7 +329,8 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
- nblocks = scan->rs_nblocks; /* # blocks to be scanned */
+ pagescan = storageam_get_heappagescandesc(scan);
+ nblocks = pagescan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
@@ -364,7 +366,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -377,7 +379,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458ec91..14120291d0 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -221,14 +221,14 @@ system_rows_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -258,7 +258,7 @@ system_rows_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -291,7 +291,7 @@ system_rows_nextsampletuple(SampleScanState *node,
*/
/* We rely on the data accumulated in pagemode access */
- Assert(scan->rs_pageatatime);
+ Assert(pagescan->rs_pageatatime);
for (;;)
{
/* Advance to next possible offset on page */
@@ -308,7 +308,7 @@ system_rows_nextsampletuple(SampleScanState *node,
}
/* Found a candidate? */
- if (SampleOffsetVisible(tupoffset, scan))
+ if (SampleOffsetVisible(tupoffset, pagescan))
{
sampler->donetuples++;
break;
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025414..aa7252215a 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
instr_time cur_time;
/* First call within scan? */
@@ -232,14 +232,14 @@ system_time_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -275,7 +275,7 @@ system_time_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6c1224db42..b8c8bf5594 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -107,8 +107,9 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_mo
* which tuples on the page are visible.
*/
void
-heapgetpage(HeapScanDesc scan, BlockNumber page)
+heapgetpage(StorageScanDesc sscan, BlockNumber page)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
Buffer buffer;
Snapshot snapshot;
Page dp;
@@ -118,13 +119,13 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
ItemId lpp;
bool all_visible;
- Assert(page < scan->rs_nblocks);
+ Assert(page < scan->rs_pagescan.rs_nblocks);
/* release previous scan buffer, if any */
- if (BufferIsValid(scan->rs_cbuf))
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
{
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
}
/*
@@ -135,20 +136,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
CHECK_FOR_INTERRUPTS();
/* read page using selected strategy */
- scan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM, page,
- RBM_NORMAL, scan->rs_strategy);
- scan->rs_cblock = page;
+ scan->rs_scan.rs_cbuf = ReadBufferExtended(scan->rs_scan.rs_rd, MAIN_FORKNUM, page,
+ RBM_NORMAL, scan->rs_pagescan.rs_strategy);
+ scan->rs_scan.rs_cblock = page;
- if (!scan->rs_pageatatime)
+ if (!scan->rs_pagescan.rs_pageatatime)
return;
- buffer = scan->rs_cbuf;
- snapshot = scan->rs_snapshot;
+ buffer = scan->rs_scan.rs_cbuf;
+ snapshot = scan->rs_scan.rs_snapshot;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
- heap_page_prune_opt(scan->rs_rd, buffer);
+ heap_page_prune_opt(scan->rs_scan.rs_rd, buffer);
/*
* We must hold share lock on the buffer content while examining tuple
@@ -158,7 +159,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
ntup = 0;
@@ -193,7 +194,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
HeapTupleData loctup;
bool valid;
- loctup.t_tableOid = RelationGetRelid(scan->rs_rd);
+ loctup.t_tableOid = RelationGetRelid(scan->rs_scan.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
@@ -201,20 +202,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, &loctup,
buffer, snapshot);
if (valid)
- scan->rs_vistuples[ntup++] = lineoff;
+ scan->rs_pagescan.rs_vistuples[ntup++] = lineoff;
}
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ scan->rs_pagescan.rs_ntuples = ntup;
}
#if defined(DISABLE_COMPLEX_MACRO)
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 4fa5ebc4e3..2a3369791c 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1745,7 +1745,7 @@ retry:
else
{
SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ sync_startpage = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
goto retry;
}
}
@@ -1787,10 +1787,10 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* starting block number, modulo nblocks.
*/
nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
+ if (nallocated >= scan->rs_pagescan.rs_nblocks)
page = InvalidBlockNumber; /* all blocks have been allocated */
else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_pagescan.rs_nblocks;
/*
* Report scan location. Normally, we report the current page number.
@@ -1799,12 +1799,12 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* doesn't slew backwards. We only report the position at the end of the
* scan once, though: subsequent callers will report nothing.
*/
- if (scan->rs_syncscan)
+ if (scan->rs_pagescan.rs_syncscan)
{
if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ ss_report_location(scan->rs_scan.rs_rd, page);
+ else if (nallocated == scan->rs_pagescan.rs_nblocks)
+ ss_report_location(scan->rs_scan.rs_rd, parallel_scan->phs_startblock);
}
return page;
@@ -1833,9 +1833,9 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* lock that ensures the interesting tuple(s) won't change.)
*/
if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ scan->rs_pagescan.rs_nblocks = scan->rs_parallel->phs_nblocks;
else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+ scan->rs_pagescan.rs_nblocks = RelationGetNumberOfBlocks(scan->rs_scan.rs_rd);
/*
* If the table is large relative to NBuffers, use a bulk-read access
@@ -1849,8 +1849,8 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* Note that heap_parallelscan_initialize has a very similar test; if you
* change this, consider changing that one, too.
*/
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
+ if (!RelationUsesLocalBuffers(scan->rs_scan.rs_rd) &&
+ scan->rs_pagescan.rs_nblocks > NBuffers / 4)
{
allow_strat = scan->rs_allow_strat;
allow_sync = scan->rs_allow_sync;
@@ -1861,20 +1861,20 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (allow_strat)
{
/* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ if (scan->rs_pagescan.rs_strategy == NULL)
+ scan->rs_pagescan.rs_strategy = GetAccessStrategy(BAS_BULKREAD);
}
else
{
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
+ scan->rs_pagescan.rs_strategy = NULL;
}
if (scan->rs_parallel != NULL)
{
/* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ scan->rs_pagescan.rs_syncscan = scan->rs_parallel->phs_syncscan;
}
else if (keep_startblock)
{
@@ -1883,25 +1883,25 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ scan->rs_pagescan.rs_syncscan = (allow_sync && synchronize_seqscans);
}
else if (allow_sync && synchronize_seqscans)
{
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ scan->rs_pagescan.rs_syncscan = true;
+ scan->rs_pagescan.rs_startblock = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
}
else
{
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
+ scan->rs_pagescan.rs_syncscan = false;
+ scan->rs_pagescan.rs_startblock = 0;
}
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
+ scan->rs_pagescan.rs_numblocks = InvalidBlockNumber;
+ scan->rs_scan.rs_inited = false;
scan->rs_ctup.t_data = NULL;
ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
/* page-at-a-time fields are always invalid when not rs_inited */
@@ -1909,7 +1909,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* copy the scan key, if appropriate
*/
if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+ memcpy(scan->rs_scan.rs_key, key, scan->rs_scan.rs_nkeys * sizeof(ScanKeyData));
/*
* Currently, we don't have a stats counter for bitmap heap scans (but the
@@ -1917,7 +1917,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* update stats for tuple fetches there)
*/
if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
+ pgstat_count_heap_scan(scan->rs_scan.rs_rd);
}
@@ -1951,7 +1951,7 @@ heapgettup(HeapScanDesc scan,
ScanKey key)
{
HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
+ Snapshot snapshot = scan->rs_scan.rs_snapshot;
bool backward = ScanDirectionIsBackward(dir);
BlockNumber page;
bool finished;
@@ -1966,14 +1966,14 @@ heapgettup(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -1986,29 +1986,29 @@ heapgettup(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc) scan, page);
lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
/* page and lineoff now reference the physically next tid */
@@ -2019,14 +2019,14 @@ heapgettup(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2037,30 +2037,30 @@ heapgettup(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc) scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineoff = lines; /* final offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
@@ -2076,20 +2076,20 @@ heapgettup(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2120,21 +2120,21 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
tuple,
snapshot,
- scan->rs_cbuf);
+ scan->rs_scan.rs_cbuf);
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, tuple,
+ scan->rs_scan.rs_cbuf, snapshot);
if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -2159,17 +2159,17 @@ heapgettup(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* advance to next/prior page and detect end of scan
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2180,10 +2180,10 @@ heapgettup(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2197,8 +2197,8 @@ heapgettup(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2206,21 +2206,21 @@ heapgettup(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc) scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber((Page) dp);
linesleft = lines;
if (backward)
@@ -2271,14 +2271,14 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2291,28 +2291,28 @@ heapgettup_pagemode(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc)scan, page);
lineindex = 0;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
+ page = scan->rs_scan.rs_cblock; /* current page */
+ lineindex = scan->rs_pagescan.rs_cindex + 1;
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
/* page and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
@@ -2322,14 +2322,14 @@ heapgettup_pagemode(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -2340,33 +2340,33 @@ heapgettup_pagemode(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc)scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineindex = lines - 1;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
- lineindex = scan->rs_cindex - 1;
+ lineindex = scan->rs_pagescan.rs_cindex - 1;
}
/* page and lineindex now reference the previous visible tid */
@@ -2377,20 +2377,20 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2399,8 +2399,8 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_len = ItemIdGetLength(lpp);
/* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+ Assert(scan->rs_pagescan.rs_cindex < scan->rs_pagescan.rs_ntuples);
+ Assert(lineoff == scan->rs_pagescan.rs_vistuples[scan->rs_pagescan.rs_cindex]);
return;
}
@@ -2413,7 +2413,7 @@ heapgettup_pagemode(HeapScanDesc scan,
{
while (linesleft > 0)
{
- lineoff = scan->rs_vistuples[lineindex];
+ lineoff = scan->rs_pagescan.rs_vistuples[lineindex];
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -2424,7 +2424,9 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
+ tuple, scan->rs_scan.rs_snapshot,
+ scan->rs_scan.rs_cbuf))
{
/*
* if current tuple qualifies, return it.
@@ -2433,19 +2435,19 @@ heapgettup_pagemode(HeapScanDesc scan,
{
bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ scan->rs_pagescan.rs_cindex = lineindex;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
else
{
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ scan->rs_pagescan.rs_cindex = lineindex;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -2464,7 +2466,7 @@ heapgettup_pagemode(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* if we get here, it means we've exhausted the items on this page and
@@ -2472,10 +2474,10 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -2486,10 +2488,10 @@ heapgettup_pagemode(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -2503,8 +2505,8 @@ heapgettup_pagemode(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -2512,21 +2514,21 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc)scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
linesleft = lines;
if (backward)
lineindex = lines - 1;
@@ -2536,7 +2538,7 @@ heapgettup_pagemode(HeapScanDesc scan,
}
-static HeapScanDesc
+static StorageScanDesc
heapam_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -2563,12 +2565,12 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
*/
scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
+ scan->rs_scan.rs_rd = relation;
+ scan->rs_scan.rs_snapshot = snapshot;
+ scan->rs_scan.rs_nkeys = nkeys;
scan->rs_bitmapscan = is_bitmapscan;
scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_pagescan.rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_temp_snap = temp_snap;
@@ -2577,7 +2579,7 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
*/
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
/*
* For a seqscan in a serializable transaction, acquire a predicate lock
@@ -2601,13 +2603,29 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* initscan() and we don't want to allocate memory again
*/
if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ scan->rs_scan.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
- scan->rs_key = NULL;
+ scan->rs_scan.rs_key = NULL;
initscan(scan, key, false);
- return scan;
+ return (StorageScanDesc) scan;
+}
+
+static ParallelHeapScanDesc
+heapam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return scan->rs_parallel;
+}
+
+static HeapPageScanDesc
+heapam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return &scan->rs_pagescan;
}
/* ----------------
@@ -2615,21 +2633,23 @@ heapam_beginscan(Relation relation, Snapshot snapshot,
* ----------------
*/
static void
-heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+heapam_rescan(StorageScanDesc sscan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
if (set_params)
{
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_scan.rs_snapshot);
}
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* reinitialize scan descriptor
@@ -2660,29 +2680,31 @@ heapam_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
* ----------------
*/
static void
-heapam_endscan(HeapScanDesc scan)
+heapam_endscan(StorageScanDesc sscan)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* decrement relation reference count and free scan descriptor storage
*/
- RelationDecrementReferenceCount(scan->rs_rd);
+ RelationDecrementReferenceCount(scan->rs_scan.rs_rd);
- if (scan->rs_key)
- pfree(scan->rs_key);
+ if (scan->rs_scan.rs_key)
+ pfree(scan->rs_scan.rs_key);
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ UnregisterSnapshot(scan->rs_scan.rs_snapshot);
pfree(scan);
}
@@ -2694,12 +2716,14 @@ heapam_endscan(HeapScanDesc scan)
* ----------------
*/
static void
-heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+heapam_scan_update_snapshot(StorageScanDesc sscan, Snapshot snapshot)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(IsMVCCSnapshot(snapshot));
RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
+ scan->rs_scan.rs_snapshot = snapshot;
scan->rs_temp_snap = true;
}
@@ -2728,7 +2752,7 @@ heapam_scan_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
static StorageTuple
-heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
+heapam_getnext(StorageScanDesc sscan, ScanDirection direction)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2736,11 +2760,11 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
HEAPAMDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2754,7 +2778,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
*/
HEAPAMDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return &(scan->rs_ctup);
}
@@ -2774,7 +2798,7 @@ heapam_getnext(HeapScanDesc sscan, ScanDirection direction)
#endif
static TupleTableSlot *
-heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+heapam_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -2782,11 +2806,11 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -2801,11 +2825,34 @@ heapam_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *
*/
HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
slot, InvalidBuffer, true);
}
+static StorageTuple
+heapam_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+ Page dp;
+ ItemId lp;
+
+ dp = (Page) BufferGetPage(scan->rs_scan.rs_cbuf);
+ lp = PageGetItemId(dp, offset);
+ Assert(ItemIdIsNormal(lp));
+
+ scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+ scan->rs_ctup.t_len = ItemIdGetLength(lp);
+ scan->rs_ctup.t_tableOid = scan->rs_scan.rs_rd->rd_id;
+ ItemPointerSet(&scan->rs_ctup.t_self, blkno, offset);
+
+ pgstat_count_heap_fetch(scan->rs_scan.rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+
+
/*
* heap_fetch - retrieve tuple with given tid
*
@@ -3123,18 +3170,19 @@ heapam_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
static void
-heapam_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+heapam_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+ Assert(!scan->rs_scan.rs_inited); /* else too late to change */
+ Assert(!scan->rs_pagescan.rs_syncscan); /* else rs_startblock is
+ * significant */
/* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+ Assert(startBlk == 0 || startBlk < scan->rs_pagescan.rs_nblocks);
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
+ scan->rs_pagescan.rs_startblock = startBlk;
+ scan->rs_pagescan.rs_numblocks = numBlks;
}
@@ -5188,12 +5236,26 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->scansetlimits = heapam_setscanlimits;
amroutine->scan_getnext = heapam_getnext;
amroutine->scan_getnextslot = heapam_getnextslot;
+ amroutine->scan_fetch_tuple_from_offset = heapam_fetch_tuple_from_offset;
amroutine->scan_end = heapam_endscan;
amroutine->scan_rescan = heapam_rescan;
amroutine->scan_update_snapshot = heapam_scan_update_snapshot;
amroutine->tuple_freeze = heapam_freeze_tuple;
amroutine->hot_search_buffer = heapam_hot_search_buffer;
+ /*
+ * The following routine needs to be provided when the storage support
+ * parallel sequential scan
+ */
+ amroutine->scan_get_parallelheapscandesc = heapam_get_parallelheapscandesc;
+
+ /*
+ * The following routine needs to be provided when the storage support
+ * BitmapHeap and Sample Scans
+ */
+ amroutine->scan_get_heappagescandesc = heapam_get_heappagescandesc;
+
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
index 0b83ecf1bc..9d6e9e9ea1 100644
--- a/src/backend/access/heap/storageam.c
+++ b/src/backend/access/heap/storageam.c
@@ -54,7 +54,7 @@
* Caller must hold a suitable lock on the correct relation.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
{
Snapshot snapshot;
@@ -67,6 +67,25 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
true, true, true, false, false, true);
}
+ParallelHeapScanDesc
+storageam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_get_parallelheapscandesc(sscan);
+}
+
+HeapPageScanDesc
+storageam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ /*
+ * Planner should have already validated whether the current storage
+ * supports Page scans are not? This function will be called only from
+ * Bitmap Heap scan and sample scan
+ */
+ Assert(sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc != NULL);
+
+ return sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc(sscan);
+}
+
/*
* heap_setscanlimits - restrict range of a heapscan
*
@@ -74,7 +93,7 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
}
@@ -104,7 +123,7 @@ storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numB
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -112,7 +131,7 @@ storage_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -122,7 +141,7 @@ storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -132,7 +151,7 @@ storage_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -140,7 +159,7 @@ storage_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -155,7 +174,7 @@ storage_beginscan_sampling(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-storage_rescan(HeapScanDesc scan,
+storage_rescan(StorageScanDesc scan,
ScanKey key)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
@@ -171,7 +190,7 @@ storage_rescan(HeapScanDesc scan,
* ----------------
*/
void
-storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
@@ -186,7 +205,7 @@ storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
* ----------------
*/
void
-storage_endscan(HeapScanDesc scan)
+storage_endscan(StorageScanDesc scan)
{
scan->rs_rd->rd_stamroutine->scan_end(scan);
}
@@ -199,23 +218,29 @@ storage_endscan(HeapScanDesc scan)
* ----------------
*/
void
-storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot)
{
scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
}
StorageTuple
-storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+storage_getnext(StorageScanDesc sscan, ScanDirection direction)
{
return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
}
TupleTableSlot *
-storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
}
+StorageTuple
+storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_fetch_tuple_from_offset(sscan, blkno, offset);
+}
+
/*
* storage_fetch - retrieve tuple with given tid
*
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 26a9ccb657..ee9352df06 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -478,10 +478,10 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
}
else
{
- HeapScanDesc scan = sysscan->scan;
+ StorageScanDesc scan = sysscan->scan;
Assert(IsMVCCSnapshot(scan->rs_snapshot));
- Assert(tup == &scan->rs_ctup);
+ /* hari Assert(tup == &scan->rs_ctup); */
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index e270cbc4a0..8793b95c08 100644
--- a/src/backend/access/tablesample/system.c
+++ b/src/backend/access/tablesample/system.c
@@ -183,7 +183,7 @@ static BlockNumber
system_nextsampleblock(SampleScanState *node)
{
SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc scan = node->pagescan;
BlockNumber nextblock = sampler->nextblock;
uint32 hashinput[2];
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index a73a363a49..da3d48b9cc 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -577,7 +577,7 @@ boot_openrel(char *relname)
int i;
struct typmap **app;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
@@ -893,7 +893,7 @@ gettype(char *type)
{
int i;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
struct typmap **app;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d2a8a06097..281e3a8e2a 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -789,7 +789,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
{
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
@@ -830,7 +830,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a201b00cae..49b75478bc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1897,7 +1897,7 @@ index_update_stats(Relation rel,
ReindexIsProcessingHeap(RelationRelationId))
{
/* don't assume syscache will work */
- HeapScanDesc pg_class_scan;
+ StorageScanDesc pg_class_scan;
ScanKeyData key[1];
ScanKeyInit(&key[0],
@@ -2210,7 +2210,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
{
bool is_system_catalog;
bool checking_uniqueness;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2649,7 +2649,7 @@ IndexCheckExclusion(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2963,7 +2963,7 @@ validate_index_heapscan(Relation heapRelation,
Snapshot snapshot,
v_i_state *state)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index a5a5268d12..259129d456 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1017,7 +1017,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
Snapshot snapshot;
TupleDesc tupdesc;
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 1d048e6394..842abcc8b5 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -151,7 +151,7 @@ RemoveConversionById(Oid conversionOid)
{
Relation rel;
HeapTuple tuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scanKeyData;
ScanKeyInit(&scanKeyData,
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 517e3101cd..63324cfc8e 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -171,7 +171,7 @@ void
DropSetting(Oid databaseid, Oid roleid)
{
Relation relsetting;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 145e3c1d65..5e3915b438 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index a51f2e4dfc..050dfa3e4c 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -377,7 +377,7 @@ void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData skey[2];
HeapTuple tup;
int nkeys = 0;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e0f6973a3f..ccdbe70ff6 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -746,7 +746,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
Datum *values;
bool *isnull;
IndexScanDesc indexScan;
- HeapScanDesc heapScan;
+ StorageScanDesc heapScan;
bool use_wal;
bool is_system_catalog;
TransactionId OldestXmin;
@@ -1638,7 +1638,7 @@ static List *
get_tables_to_cluster(MemoryContext cluster_context)
{
Relation indRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData entry;
HeapTuple indexTuple;
Form_pg_index index;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index cad0eed1a4..7e050b0a96 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2030,7 +2030,7 @@ CopyTo(CopyState cstate)
{
Datum *values;
bool *nulls;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 39850b1b37..09135774c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -99,7 +99,7 @@ static int errdetail_busy_db(int notherbackends, int npreparedxacts);
Oid
createdb(ParseState *pstate, const CreatedbStmt *stmt)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
Relation rel;
Oid src_dboid;
Oid src_owner;
@@ -1872,7 +1872,7 @@ static void
remove_dbtablespaces(Oid db_id)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
@@ -1939,7 +1939,7 @@ check_db_file_conflict(Oid db_id)
{
bool result = false;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 5209083a72..b8440f8e73 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1874,7 +1874,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{
Oid objectOid;
Relation relationRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scan_keys[1];
HeapTuple tuple;
MemoryContext private_context;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3bab05079e..d7ea804370 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4492,7 +4492,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
@@ -5065,7 +5065,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
@@ -8216,7 +8216,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Expr *origexpr;
ExprState *exprstate;
TupleDesc tupdesc;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
MemoryContext oldcxt;
@@ -8299,7 +8299,7 @@ validateForeignKeyConstraint(char *conname,
Oid pkindOid,
Oid constraintOid)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
@@ -10806,7 +10806,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ListCell *l;
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index cdfa8ffb3f..be3c0db9e2 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -402,7 +402,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
char *tablespacename = stmt->tablespacename;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
Relation rel;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -913,7 +913,7 @@ RenameTableSpace(const char *oldname, const char *newname)
Oid tspId;
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
Form_pg_tablespace newform;
@@ -988,7 +988,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
{
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tup;
Oid tablespaceoid;
Datum datum;
@@ -1382,7 +1382,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
{
Oid result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -1428,7 +1428,7 @@ get_tablespace_name(Oid spc_oid)
{
char *result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index b95e3fc5ab..273bca0c2b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2382,7 +2382,7 @@ AlterDomainNotNull(List *names, bool notNull)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
@@ -2778,7 +2778,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bafc15eac6..16622f3bee 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -529,7 +529,7 @@ get_all_vacuum_rels(void)
{
List *vacrels = NIL;
Relation pgclass;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
@@ -1183,7 +1183,7 @@ vac_truncate_clog(TransactionId frozenXID,
{
TransactionId nextXID = ReadNewTransactionId();
Relation relation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid oldestxid_datoid;
Oid minmulti_datoid;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index a661323fda..07d493b0f8 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -55,14 +55,14 @@
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
-static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
+static void bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres);
static inline void BitmapDoneInitializingSharedState(
ParallelBitmapHeapState *pstate);
static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node,
TBMIterateResult *tbmres);
static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
static inline void BitmapPrefetch(BitmapHeapScanState *node,
- HeapScanDesc scan);
+ StorageScanDesc scan);
static bool BitmapShouldInitializeSharedState(
ParallelBitmapHeapState *pstate);
@@ -77,7 +77,8 @@ static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
TIDBitmap *tbm;
TBMIterator *tbmiterator = NULL;
TBMSharedIterator *shared_tbmiterator = NULL;
@@ -93,6 +94,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scan = node->ss.ss_currentScanDesc;
+ pagescan = node->pagescan;
tbm = node->tbm;
if (pstate == NULL)
tbmiterator = node->tbmiterator;
@@ -192,8 +194,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
for (;;)
{
- Page dp;
- ItemId lp;
+ StorageTuple tuple;
CHECK_FOR_INTERRUPTS();
@@ -220,7 +221,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
- if (tbmres->blockno >= scan->rs_nblocks)
+ if (tbmres->blockno >= pagescan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
@@ -243,14 +244,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
* The number of tuples on this page is put into
* scan->rs_ntuples; note we don't fill scan->rs_vistuples.
*/
- scan->rs_ntuples = tbmres->ntuples;
+ pagescan->rs_ntuples = tbmres->ntuples;
}
else
{
/*
* Fetch the current heap page and identify candidate tuples.
*/
- bitgetpage(scan, tbmres);
+ bitgetpage(node, tbmres);
}
if (tbmres->ntuples >= 0)
@@ -261,7 +262,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
- scan->rs_cindex = 0;
+ pagescan->rs_cindex = 0;
/* Adjust the prefetch target */
BitmapAdjustPrefetchTarget(node);
@@ -271,7 +272,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
- scan->rs_cindex++;
+ pagescan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -298,7 +299,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
- if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
+ if (pagescan->rs_cindex < 0 || pagescan->rs_cindex >= pagescan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
continue;
@@ -325,23 +326,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple.
*/
- targoffset = scan->rs_vistuples[scan->rs_cindex];
- dp = (Page) BufferGetPage(scan->rs_cbuf);
- lp = PageGetItemId(dp, targoffset);
- Assert(ItemIdIsNormal(lp));
-
- scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- scan->rs_ctup.t_len = ItemIdGetLength(lp);
- scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
-
- pgstat_count_heap_fetch(scan->rs_rd);
+ targoffset = pagescan->rs_vistuples[pagescan->rs_cindex];
+ tuple = storage_fetch_tuple_from_offset(scan, tbmres->blockno, targoffset);
/*
* Set up the result slot to point to this tuple. Note that the
* slot acquires a pin on the buffer.
*/
- ExecStoreTuple(&scan->rs_ctup,
+ ExecStoreTuple(tuple,
slot,
scan->rs_cbuf,
false);
@@ -383,8 +375,10 @@ BitmapHeapNext(BitmapHeapScanState *node)
* interesting according to the bitmap, and visible according to the snapshot.
*/
static void
-bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
+bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres)
{
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
BlockNumber page = tbmres->blockno;
Buffer buffer;
Snapshot snapshot;
@@ -393,7 +387,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
- Assert(page < scan->rs_nblocks);
+ Assert(page < pagescan->rs_nblocks);
scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
scan->rs_rd,
@@ -436,7 +430,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
ItemPointerSet(&tid, page, offnum);
if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
- scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
+ pagescan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
@@ -452,23 +446,20 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
{
ItemId lp;
- HeapTupleData loctup;
+ StorageTuple loctup;
bool valid;
lp = PageGetItemId(dp, offnum);
if (!ItemIdIsNormal(lp))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- loctup.t_len = ItemIdGetLength(lp);
- loctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ loctup = storage_fetch_tuple_from_offset(scan, page, offnum);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, loctup, snapshot, buffer);
if (valid)
{
- scan->rs_vistuples[ntup++] = offnum;
- PredicateLockTuple(scan->rs_rd, &loctup, snapshot);
+ pagescan->rs_vistuples[ntup++] = offnum;
+ PredicateLockTuple(scan->rs_rd, loctup, snapshot);
}
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_rd, loctup,
buffer, snapshot);
}
}
@@ -476,7 +467,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ pagescan->rs_ntuples = ntup;
}
/*
@@ -602,7 +593,7 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
* BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
*/
static inline void
-BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
+BitmapPrefetch(BitmapHeapScanState *node, StorageScanDesc scan)
{
#ifdef USE_PREFETCH
ParallelBitmapHeapState *pstate = node->pstate;
@@ -793,7 +784,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -958,6 +949,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
0,
NULL);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
+
/*
* get the scan type from the relation descriptor.
*/
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 64f3dd8be0..064b655aea 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -31,9 +31,7 @@ static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
static StorageTuple tablesample_getnext(SampleScanState *scanstate);
static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
-
-//hari
+ SampleScanState *scanstate);
/* ----------------------------------------------------------------
* Scan Support
@@ -357,6 +355,7 @@ tablesample_init(SampleScanState *scanstate)
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
}
else
{
@@ -382,10 +381,11 @@ static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+ StorageTuple tuple;
Snapshot snapshot = scan->rs_snapshot;
- bool pagemode = scan->rs_pageatatime;
+ bool pagemode = pagescan->rs_pageatatime;
BlockNumber blockno;
Page page;
bool all_visible;
@@ -396,10 +396,9 @@ tablesample_getnext(SampleScanState *scanstate)
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
{
Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
return NULL;
}
if (tsm->NextSampleBlock)
@@ -407,13 +406,12 @@ tablesample_getnext(SampleScanState *scanstate)
blockno = tsm->NextSampleBlock(scanstate);
if (!BlockNumberIsValid(blockno))
{
- tuple->t_data = NULL;
return NULL;
}
}
else
- blockno = scan->rs_startblock;
- Assert(blockno < scan->rs_nblocks);
+ blockno = pagescan->rs_startblock;
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
scan->rs_inited = true;
}
@@ -456,14 +454,12 @@ tablesample_getnext(SampleScanState *scanstate)
if (!ItemIdIsNormal(itemid))
continue;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
- tuple->t_len = ItemIdGetLength(itemid);
- ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
+ tuple = storage_fetch_tuple_from_offset(scan, blockno, tupoffset);
if (all_visible)
visible = true;
else
- visible = SampleTupleVisible(tuple, tupoffset, scan);
+ visible = SampleTupleVisible(tuple, tupoffset, scanstate);
/* in pagemode, heapgetpage did this for us */
if (!pagemode)
@@ -494,14 +490,14 @@ tablesample_getnext(SampleScanState *scanstate)
if (tsm->NextSampleBlock)
{
blockno = tsm->NextSampleBlock(scanstate);
- Assert(!scan->rs_syncscan);
+ Assert(!pagescan->rs_syncscan);
finished = !BlockNumberIsValid(blockno);
}
else
{
/* Without NextSampleBlock, just do a plain forward seqscan. */
blockno++;
- if (blockno >= scan->rs_nblocks)
+ if (blockno >= pagescan->rs_nblocks)
blockno = 0;
/*
@@ -514,10 +510,10 @@ tablesample_getnext(SampleScanState *scanstate)
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
+ if (pagescan->rs_syncscan)
ss_report_location(scan->rs_rd, blockno);
- finished = (blockno == scan->rs_startblock);
+ finished = (blockno == pagescan->rs_startblock);
}
/*
@@ -529,12 +525,11 @@ tablesample_getnext(SampleScanState *scanstate)
ReleaseBuffer(scan->rs_cbuf);
scan->rs_cbuf = InvalidBuffer;
scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
scan->rs_inited = false;
return NULL;
}
- Assert(blockno < scan->rs_nblocks);
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
/* Re-establish state for new page */
@@ -549,16 +544,19 @@ tablesample_getnext(SampleScanState *scanstate)
/* Count successfully-fetched tuples as heap fetches */
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return tuple;
}
/*
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, SampleScanState *scanstate)
{
- if (scan->rs_pageatatime)
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+
+ if (pagescan->rs_pageatatime)
{
/*
* In pageatatime mode, heapgetpage() already did visibility checks,
@@ -570,12 +568,12 @@ SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan
* gain to justify the restriction.
*/
int start = 0,
- end = scan->rs_ntuples - 1;
+ end = pagescan->rs_ntuples - 1;
while (start <= end)
{
int mid = (start + end) / 2;
- OffsetNumber curoffset = scan->rs_vistuples[mid];
+ OffsetNumber curoffset = pagescan->rs_vistuples[mid];
if (tupoffset == curoffset)
return true;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 0521df1fa9..ac3754d91d 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -317,9 +317,10 @@ void
ExecSeqScanReInitializeDSM(SeqScanState *node,
ParallelContext *pcxt)
{
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ ParallelHeapScanDesc pscan;
- heap_parallelscan_reinitialize(scan->rs_parallel);
+ pscan = storageam_get_parallelheapscandesc(node->ss.ss_currentScanDesc);
+ heap_parallelscan_reinitialize(pscan);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 9d35a41e22..aa7808001d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xlog.h"
@@ -253,7 +254,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amsearchnulls = amroutine->amsearchnulls;
info->amcanparallel = amroutine->amcanparallel;
info->amhasgettuple = (amroutine->amgettuple != NULL);
- info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amhasgetbitmap = ((amroutine->amgetbitmap != NULL)
+ && (relation->rd_stamroutine->scan_get_heappagescandesc != NULL));
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
@@ -1825,7 +1827,7 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
Relation relation)
{
PartitionDesc partdesc;
- PartitionKey partkey;
+ PartitionKey partkey;
Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
@@ -1890,8 +1892,8 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
/*
* Did not find matching partition scheme. Create one copying relevant
- * information from the relcache. We need to copy the contents of the array
- * since the relcache entry may not survive after we have closed the
+ * information from the relcache. We need to copy the contents of the
+ * array since the relcache entry may not survive after we have closed the
* relation.
*/
part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData));
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0d2e1733bf..ee005d4168 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1865,7 +1865,7 @@ get_database_list(void)
{
List *dblist = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
@@ -1931,7 +1931,7 @@ do_autovacuum(void)
{
Relation classRel;
HeapTuple tuple;
- HeapScanDesc relScan;
+ StorageScanDesc relScan;
Form_pg_database dbForm;
List *table_oids = NIL;
List *orphan_oids = NIL;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 36808c4067..db4b091471 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1207,7 +1207,7 @@ pgstat_collect_oids(Oid catalogid)
HTAB *htab;
HASHCTL hash_ctl;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 83ec2dfcbe..73bb1770f1 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -107,7 +107,7 @@ get_subscription_list(void)
{
List *res = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4924daca76..7122987ad7 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -420,7 +420,7 @@ DefineQueryRewrite(char *rulename,
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
Snapshot snapshot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 03b7cc76d7..42a35824e5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1208,7 +1208,7 @@ static bool
ThereIsAtLeastOneRole(void)
{
Relation pg_authid_rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
bool result;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4cddd73355..42c26861de 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -97,6 +97,8 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
#define heap_close(r,l) relation_close(r,l)
/* struct definitions appear in relscan.h */
+typedef struct HeapPageScanDescData *HeapPageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
@@ -106,7 +108,7 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
+extern void heapgetpage(StorageScanDesc sscan, BlockNumber page);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 147f862a2b..2651fd73d7 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/htup_details.h"
#include "access/itup.h"
#include "access/tupdesc.h"
@@ -42,40 +43,54 @@ typedef struct ParallelHeapScanDescData
char phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelHeapScanDescData;
-typedef struct HeapScanDescData
+typedef struct StorageScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
Snapshot rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
ScanKey rs_key; /* array of scan key descriptors */
- bool rs_bitmapscan; /* true if this is really a bitmap scan */
- bool rs_samplescan; /* true if this is really a sample scan */
+
+ /* scan current state */
+ bool rs_inited; /* false = scan not init'd yet */
+ BlockNumber rs_cblock; /* current block # in scan, if any */
+ Buffer rs_cbuf; /* current buffer in scan, if any */
+} StorageScanDescData;
+
+typedef struct HeapPageScanDescData
+{
bool rs_pageatatime; /* verify visibility page-at-a-time? */
- bool rs_allow_strat; /* allow or disallow use of access strategy */
- bool rs_allow_sync; /* allow or disallow use of syncscan */
- bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* total number of blocks in rel */
BlockNumber rs_startblock; /* block # to start at */
BlockNumber rs_numblocks; /* max number of blocks to scan */
+
/* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */
BufferAccessStrategy rs_strategy; /* access strategy for reads */
bool rs_syncscan; /* report location to syncscan logic? */
- /* scan current state */
- bool rs_inited; /* false = scan not init'd yet */
- HeapTupleData rs_ctup; /* current tuple in scan, if any */
- BlockNumber rs_cblock; /* current block # in scan, if any */
- Buffer rs_cbuf; /* current buffer in scan, if any */
- /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- ParallelHeapScanDesc rs_parallel; /* parallel scan information */
-
/* these fields only used in page-at-a-time mode and for bitmap scans */
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
+} HeapPageScanDescData;
+
+typedef struct HeapScanDescData
+{
+ /* scan parameters */
+ StorageScanDescData rs_scan; /* */
+ HeapPageScanDescData rs_pagescan;
+ bool rs_bitmapscan; /* true if this is really a bitmap scan */
+ bool rs_samplescan; /* true if this is really a sample scan */
+ bool rs_allow_strat; /* allow or disallow use of access strategy */
+ bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
+
+ HeapTupleData rs_ctup; /* current tuple in scan, if any */
+
+ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+ ParallelHeapScanDesc rs_parallel; /* parallel scan information */
} HeapScanDescData;
/*
@@ -149,12 +164,12 @@ typedef struct ParallelIndexScanDescData
char ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelIndexScanDescData;
-/* Struct for heap-or-index scans of system tables */
+/* Struct for storage-or-index scans of system tables */
typedef struct SysScanDescData
{
Relation heap_rel; /* catalog being scanned */
Relation irel; /* NULL if doing heap scan */
- HeapScanDesc scan; /* only valid in heap-scan case */
+ StorageScanDesc scan; /* only valid in storage-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index ec5dc89a26..4c1ff4cea5 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -20,7 +20,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
-typedef void *StorageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef union tuple_data
{
@@ -43,29 +43,31 @@ typedef enum tuple_data_flags
typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
bool *specConflict, List *arbiterIndexes);
-extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
-
-extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
-extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+extern StorageScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+extern ParallelHeapScanDesc storageam_get_parallelheapscandesc(StorageScanDesc sscan);
+extern HeapPageScanDesc storageam_get_heappagescandesc(StorageScanDesc sscan);
+extern void storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern StorageScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-
-extern void storage_endscan(HeapScanDesc scan);
-extern void storage_rescan(HeapScanDesc scan, ScanKey key);
-extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern StorageScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern StorageScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(StorageScanDesc scan);
+extern void storage_rescan(StorageScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
-extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
-extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_getnext(StorageScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset);
extern void storage_get_latest_tid(Relation relation,
Snapshot snapshot,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 6e790bb21a..346f0ce0da 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -20,32 +20,37 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
-typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+typedef StorageScanDesc(*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef ParallelHeapScanDesc (*ScanGetParallelheapscandesc_function) (StorageScanDesc scan);
+typedef HeapPageScanDesc(*ScanGetHeappagescandesc_function) (StorageScanDesc scan);
+
+typedef void (*ScanSetlimits_function) (StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
/* must return a TupleTableSlot? */
-typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+typedef StorageTuple(*ScanGetnext_function) (StorageScanDesc scan,
ScanDirection direction);
-typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (StorageScanDesc scan,
ScanDirection direction, TupleTableSlot *slot);
+typedef StorageTuple(*ScanFetchTupleFromOffset_function) (StorageScanDesc scan,
+ BlockNumber blkno, OffsetNumber offset);
-typedef void (*ScanEnd_function) (HeapScanDesc scan);
+typedef void (*ScanEnd_function) (StorageScanDesc scan);
-typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
-typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+typedef void (*ScanGetpage_function) (StorageScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+typedef void (*ScanUpdateSnapshot_function) (StorageScanDesc scan, Snapshot snapshot);
typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
@@ -158,9 +163,12 @@ typedef struct StorageAmRoutine
/* Operations on relation scans */
ScanBegin_function scan_begin;
+ ScanGetParallelheapscandesc_function scan_get_parallelheapscandesc;
+ ScanGetHeappagescandesc_function scan_get_heappagescandesc;
ScanSetlimits_function scansetlimits;
ScanGetnext_function scan_getnext;
ScanGetnextSlot_function scan_getnextslot;
+ ScanFetchTupleFromOffset_function scan_fetch_tuple_from_offset;
ScanEnd_function scan_end;
ScanGetpage_function scan_getpage;
ScanRescan_function scan_rescan;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9d1549115d..a40cfdd71e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1102,7 +1102,7 @@ typedef struct ScanState
{
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
- HeapScanDesc ss_currentScanDesc;
+ StorageScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
@@ -1123,6 +1123,7 @@ typedef struct SeqScanState
typedef struct SampleScanState
{
ScanState ss;
+ HeapPageScanDesc pagescan;
List *args; /* expr states for TABLESAMPLE params */
ExprState *repeatable; /* expr state for REPEATABLE expr */
/* use struct pointer to avoid including tsmapi.h here */
@@ -1351,6 +1352,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
+ HeapPageScanDesc pagescan;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
--
2.14.2.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.3K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/5-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From 534a5d11912224bcd37e183e25f4340e60481e65 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/8] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 1 +
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa0fd..33079c1c16 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4c83a63f7d..278a6e8e1c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5174,16 +5176,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..0a7e0a33e8 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b894..dd9c263ade 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031aad7..4a8ab151d7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3877,6 +3877,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index ffdb452b02..ea352fa247 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 684f7f20a8..3113966415 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1713,11 +1713,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e8fdf8454d..bb1570c94f 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1155,15 +1155,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.14.2.windows.1
[application/octet-stream] 0002-Storage-AM-API-hooks-and-related-functions.patch (18.6K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/6-0002-Storage-AM-API-hooks-and-related-functions.patch)
download | inline diff:
From 6ced949e8d427880eeb974ae3b7aa3ca9d83a3dd Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/8] Storage AM API hooks and related functions
Basic storage API functions are added, these
will change in the further patches
---
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 58 ++++++++
src/backend/access/heap/storageamapi.c | 103 ++++++++++++++
src/include/access/htup.h | 21 +++
src/include/access/storageamapi.h | 223 +++++++++++++++++++++++++++++++
src/include/catalog/pg_am.h | 3 +
src/include/catalog/pg_proc.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/utils/tqual.h | 9 --
9 files changed, 416 insertions(+), 10 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/heap/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496bcd..02a3909e7c 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000000..88827e7957
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageamapi.h"
+#include "access/subtrans.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/heap/storageamapi.c b/src/backend/access/heap/storageamapi.c
new file mode 100644
index 0000000000..bcbe14588b
--- /dev/null
+++ b/src/backend/access/heap/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine * HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 61b3e68639..6459435c78 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -26,6 +26,27 @@ typedef struct MinimalTupleData MinimalTupleData;
typedef MinimalTupleData *MinimalTuple;
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000000..90b16ccd0a
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,223 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*ScanEnd_function) (HeapScanDesc scan);
+
+
+typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+typedef Oid (*TupleInsert_function) (Relation relation,
+ TupleTableSlot *tupslot,
+ CommandId cid,
+ int options,
+ BulkInsertState bistate);
+
+typedef HTSU_Result (*TupleDelete_function) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode);
+
+typedef bool (*TupleFetch_function) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*TupleLock_function) (Relation relation,
+ ItemPointer tid,
+ StorageTuple * tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*MultiInsert_function) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef bool (*TupleFreeze_function) (HeapTupleHeader tuple, TransactionId cutoff_xid,
+ TransactionId cutoff_multi);
+
+typedef void (*TupleGetLatestTid_function) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data(*GetTupleData_function) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple(*TupleFromDatum_function) (Datum data, Oid tableoid);
+
+typedef void (*SlotStoreTuple_function) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*SlotClearTuple_function) (TupleTableSlot *slot);
+typedef Datum (*SlotGetattr_function) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*SlotVirtualizeTuple_function) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*SlotGetTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*SpeculativeAbort_function) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*RelationSync_function) (Relation relation);
+
+typedef bool (*SnapshotSatisfies_function) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ SlotStoreTuple_function slot_store_tuple;
+ SlotVirtualizeTuple_function slot_virtualize_tuple;
+ SlotClearTuple_function slot_clear_tuple;
+ SlotGetattr_function slot_getattr;
+ SlotGetTuple_function slot_tuple;
+ SlotGetMinTuple_function slot_min_tuple;
+ SlotUpdateTableoid_function slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine * (*slot_storageam_hook) (void);
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+ /* Operations on relation scans */
+ ScanBegin_function scan_begin;
+ ScanSetlimits_function scansetlimits;
+ ScanGetnext_function scan_getnext;
+ ScanGetnextSlot_function scan_getnextslot;
+ ScanEnd_function scan_end;
+ ScanGetpage_function scan_getpage;
+ ScanRescan_function scan_rescan;
+ ScanUpdateSnapshot_function scan_update_snapshot;
+ HotSearchBuffer_function hot_search_buffer; /* heap_hot_search_buffer */
+
+ /* Operations on physical tuples */
+ TupleInsert_function tuple_insert; /* heap_insert */
+ TupleUpdate_function tuple_update; /* heap_update */
+ TupleDelete_function tuple_delete; /* heap_delete */
+ TupleFetch_function tuple_fetch; /* heap_fetch */
+ TupleLock_function tuple_lock; /* heap_lock_tuple */
+ MultiInsert_function multi_insert; /* heap_multi_insert */
+ TupleFreeze_function tuple_freeze; /* heap_freeze_tuple */
+ TupleGetLatestTid_function tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ GetTupleData_function get_tuple_data;
+ TupleFromDatum_function tuple_from_datum;
+
+ slot_storageam_hook slot_storageam;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no
+ * need for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ SpeculativeAbort_function speculative_abort;
+
+ RelationSync_function relation_sync; /* heap_sync */
+
+ SnapshotSatisfies_function snapshot_satisfies;
+ SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+} StorageAmRoutine;
+
+extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine * GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine * GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index dd9c263ade..2c3e33c104 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -84,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4a8ab151d7..f1d0198848 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -558,6 +558,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb4919b..805d623cf0 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -497,6 +497,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 96eaf01ca0..4fda7f8384 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -45,15 +45,6 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
/* These are the "satisfies" test routines for the various snapshot types */
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
--
2.14.2.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/7-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From 0fab36983accfec69748a4b47639c4b8771bdbaa Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/8] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 119 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 130 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index b8e37809b0..179906a16f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,6 +34,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1368,10 +1369,27 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1868,6 +1886,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2026,6 +2109,11 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
RelationInitPhysicalAddr(relation);
+ /*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
/*
* initialize the rel-has-index flag, using hardwired knowledge
*/
@@ -2354,6 +2442,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3368,6 +3458,14 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3889,6 +3987,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert(relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5607,6 +5717,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5380..82d8a534b1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -160,6 +160,12 @@ typedef struct RelationData
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
+ /*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
/*
* index access support info (used only for an index relation)
*
@@ -427,6 +433,12 @@ typedef struct ViewOptions
*/
#define RelationGetDescr(relation) ((relation)->rd_att)
+/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
/*
* RelationGetRelationName
* Returns the rel's name.
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 3c53cefe4b..03d996f5a1 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -71,6 +71,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.14.2.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (149.4K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/8-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From 86a61d036c6fa456a18534204227e133e89953ea Mon Sep 17 00:00:00 2001
From: Hari Babu Kommi <kommi.haribabu@gmail.com>
Date: Fri, 8 Sep 2017 15:38:28 +1000
Subject: [PATCH 4/8] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and heapam storage is moved into heapam_common.c
and heapam_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 11 +-
contrib/pgrowlocks/pgrowlocks.c | 7 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam.c | 62 +-
src/backend/access/heap/heapam_common.c | 160 +++
src/backend/access/heap/heapam_storage.c | 1644 ++++++++++++++++++++++++
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/access/index/genam.c | 4 +-
src/backend/catalog/index.c | 6 +-
src/backend/commands/analyze.c | 6 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 6 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 11 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/backend/utils/time/tqual.c | 1807 ---------------------------
src/include/access/heapam_common.h | 96 ++
src/include/access/htup.h | 7 +-
src/include/storage/bufmgr.h | 6 +-
src/include/utils/snapshot.h | 2 +-
src/include/utils/tqual.h | 65 +-
29 files changed, 2023 insertions(+), 1925 deletions(-)
create mode 100644 src/backend/access/heap/heapam_common.c
delete mode 100644 src/backend/utils/time/tqual.c
create mode 100644 src/include/access/heapam_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575d9f..01ff3fed5d 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -11,6 +11,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/visibilitymap.h"
#include "catalog/pg_type.h"
#include "catalog/storage_xlog.h"
@@ -51,7 +52,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +657,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +682,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +740,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65bd2..830e74fd07 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,9 +150,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
- GetCurrentCommandId(false),
- scan->rs_cbuf);
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
+ GetCurrentCommandId(false),
+ scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
infomask = tuple->t_data->t_infomask;
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf06138a5..284eabc970 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7ca1bb24d2..e098202f84 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 02a3909e7c..e6bc18e5ea 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o storageamapi.o \
+OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
+ pruneheap.o rewriteheap.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 765750b874..f62f04e185 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -40,11 +40,13 @@
#include "access/bufmask.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +440,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +655,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -841,6 +844,7 @@ heapgettup_pagemode(HeapScanDesc scan,
lineindex = scan->rs_cindex + 1;
}
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -885,6 +889,7 @@ heapgettup_pagemode(HeapScanDesc scan,
page = scan->rs_cblock; /* current page */
}
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -954,23 +959,31 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (key != NULL)
+ if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
{
- bool valid;
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+ else
{
scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
/*
* otherwise move to the next item on the page
@@ -982,6 +995,12 @@ heapgettup_pagemode(HeapScanDesc scan,
++lineindex;
}
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
/*
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
@@ -1039,6 +1058,7 @@ heapgettup_pagemode(HeapScanDesc scan,
heapgetpage(scan, page);
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -1831,7 +1851,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return heap_copytuple(&(scan->rs_ctup));
}
/*
@@ -1950,7 +1970,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2097,7 +2117,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2271,7 +2291,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3097,7 +3117,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3208,7 +3228,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3668,7 +3688,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3849,7 +3869,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4600,7 +4620,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
new file mode 100644
index 0000000000..4cc3eeff0c
--- /dev/null
+++ b/src/backend/access/heap/heapam_common.c
@@ -0,0 +1,160 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_common.c
+ * heapam access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_common.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_common.h"
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return FALSE when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 88827e7957..f7e11d4946 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/hio.h"
#include "access/htup_details.h"
@@ -47,12 +48,1655 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ *
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted (and it is safe to set the hint bits).
+ * If the hint bits are changed, MarkBufferDirtyHint is called on
+ * the passed-in buffer. The caller must hold not only a pin, but at least
+ * shared buffer content lock on the buffer containing the tuple.
+ *
+ * NOTE: When using a non-MVCC snapshot, we must check
+ * TransactionIdIsInProgress (which looks in the PGXACT array)
+ * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
+ * pg_xact). Otherwise we have a race condition: we might decide that a
+ * just-committed transaction crashed, because none of the tests succeed.
+ * xact.c is careful to record commit/abort in pg_xact before it unsets
+ * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
+ * also means there is a window where TransactionIdIsInProgress and
+ * TransactionIdDidCommit will both return true. If we check only
+ * TransactionIdDidCommit, we could consider a tuple committed when a
+ * later GetSnapshotData call will still think the originating transaction
+ * is in progress, which leads to application-level inconsistency. The
+ * upshot is that we gotta check TransactionIdIsInProgress first in all
+ * code paths, except for a few cases where we are looking at
+ * subtransactions of our own main transaction and so there can't be any
+ * race condition.
+ *
+ * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
+ * TransactionIdIsInProgress, but the logic is otherwise the same: do not
+ * check pg_xact until after deciding that the xact is no longer in progress.
+ *
+ *
+ * Summary of visibility functions:
+ *
+ * HeapTupleSatisfiesMVCC()
+ * visible to supplied snapshot, excludes current command
+ * HeapTupleSatisfiesUpdate()
+ * visible to instant snapshot, with user-supplied command
+ * counter and more complex result
+ * HeapTupleSatisfiesSelf()
+ * visible to instant snapshot and current command
+ * HeapTupleSatisfiesDirty()
+ * like HeapTupleSatisfiesSelf(), but includes open transactions
+ * HeapTupleSatisfiesVacuum()
+ * visible to any running transaction, used by VACUUM
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesToast()
+ * visible unless part of interrupted vacuum, used for TOAST
+ * HeapTupleSatisfiesAny()
+ * all tuples are visible
+ *
+ * -------------------------------------------------------------------------
+ */
+
+/*
+ * HeapTupleSatisfiesSelf
+ * True iff heap tuple is valid "for itself".
+ *
+ * Here, we consider the effects of:
+ * all committed transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * Note:
+ * Assumes heap tuple is valid.
+ *
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && the row was updated by the current transaction, and
+ * (Xmax is null it was not deleted
+ * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
+ * ||
+ *
+ * (Xmin is committed && the row was modified by a committed transaction, and
+ * (Xmax is null || the row has not been deleted, or
+ * (Xmax != my-transaction && the row was deleted by another transaction
+ * Xmax is not committed))) that has not been committed
+ */
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false;
+}
+
+/*
+ * HeapTupleSatisfiesAny
+ * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesToast
+ * True iff heap tuple is valid as a TOAST row.
+ *
+ * This is a simplified version that only checks for VACUUM moving conditions.
+ * It's appropriate for TOAST usage because TOAST really doesn't want to do
+ * its own time qual checks; if you can see the main table row that contains
+ * a TOAST reference, you should be able to see the TOASTed value. However,
+ * vacuuming a TOAST table is independent of the main table, and in case such
+ * a vacuum fails partway through, we'd better do this much checking.
+ *
+ * Among other things, this means you can't do UPDATEs of rows in a TOAST
+ * table.
+ */
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+
+ /*
+ * An invalid Xmin can be left behind by a speculative insertion that
+ * is canceled by super-deleting the tuple. This also applies to
+ * TOAST tuples created during speculative insertion.
+ */
+ else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ return false;
+ }
+
+ /* otherwise assume the tuple is valid for TOAST. */
+ return true;
+}
+
+/*
+ * HeapTupleSatisfiesUpdate
+ *
+ * This function returns a more detailed result code than most of the
+ * functions in this file, since UPDATE needs to know more than "is it
+ * visible?". It also allows for user-supplied CommandId rather than
+ * relying on CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction. (Note: this includes
+ * the case where the tuple is share-locked by a MultiXact, even if the
+ * MultiXact includes the current transaction. Callers that want to
+ * distinguish that case must test for it themselves.)
+ */
+static HTSU_Result
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HeapTupleInvisible;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HeapTupleInvisible;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return HeapTupleInvisible;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ return HeapTupleInvisible; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ /*
+ * Careful here: even though this tuple was created by our own
+ * transaction, it might be locked by other transactions, if
+ * the original version was key-share locked when we updated
+ * it.
+ */
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (MultiXactIdIsRunning(xmax, true))
+ return HeapTupleBeingUpdated;
+ else
+ return HeapTupleMayBeUpdated;
+ }
+
+ /*
+ * If the locker is gone, then there is nothing of interest
+ * left in this Xmax; otherwise, report the tuple as
+ * locked/updated.
+ */
+ if (!TransactionIdIsInProgress(xmax))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleBeingUpdated;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* deleting subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ false))
+ return HeapTupleBeingUpdated;
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan
+ * started */
+ else
+ return HeapTupleInvisible; /* updated before scan
+ * started */
+ }
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ return HeapTupleInvisible;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HeapTupleInvisible;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return HeapTupleMayBeUpdated;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+ return HeapTupleUpdated; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
+ return HeapTupleMayBeUpdated;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ return HeapTupleBeingUpdated;
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+ if (!TransactionIdIsValid(xmax))
+ {
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+ }
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ return HeapTupleBeingUpdated;
+
+ if (TransactionIdDidCommit(xmax))
+ return HeapTupleUpdated;
+
+ /*
+ * By here, the update in the Xmax is either aborted or crashed, but
+ * what about the other members?
+ */
+
+ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /*
+ * There's no member, even just a locker, alive anymore, so we can
+ * mark the Xmax as invalid.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+ else
+ {
+ /* There are lockers running */
+ return HeapTupleBeingUpdated;
+ }
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return HeapTupleBeingUpdated;
+ if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ return HeapTupleSelfUpdated; /* updated after scan started */
+ else
+ return HeapTupleInvisible; /* updated before scan started */
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HeapTupleBeingUpdated;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HeapTupleMayBeUpdated;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return HeapTupleUpdated; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesDirty
+ * True iff heap tuple is valid including effects of open transactions.
+ *
+ * Here, we consider the effects of:
+ * all committed and in-progress transactions (as of the current instant)
+ * previous commands of this transaction
+ * changes made by the current command
+ *
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
+ * the current transaction and committed/aborted xacts are concerned.
+ * However, we also include the effects of other xacts still in progress.
+ *
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple. snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.
+ * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
+ * inserted speculatively, meaning that the inserter might still back down
+ * on the insertion without aborting the whole transaction, the associated
+ * token is also returned in snapshot->speculativeToken.
+ */
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ snapshot->xmin = snapshot->xmax = InvalidTransactionId;
+ snapshot->speculativeToken = 0;
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!TransactionIdIsInProgress(xvac))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (TransactionIdIsInProgress(xvac))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else
+ return false;
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ return false;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * Return the speculative token to caller. Caller can worry about
+ * xmax, since it requires a conclusively locked row version, and
+ * a concurrent update to this tuple is a conflict of its
+ * purposes.
+ */
+ if (HeapTupleHeaderIsSpeculative(tuple))
+ {
+ snapshot->speculativeToken =
+ HeapTupleHeaderGetSpeculativeToken(tuple);
+
+ Assert(snapshot->speculativeToken != 0);
+ }
+
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ /* XXX shouldn't we fall through to look at xmax? */
+ return true; /* in insertion by other */
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false; /* updated by other */
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ {
+ snapshot->xmax = xmax;
+ return true;
+ }
+ if (TransactionIdDidCommit(xmax))
+ return false;
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+ return false;
+ }
+
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ return true;
+ }
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ return false; /* updated by other */
+}
+
+/*
+ * HeapTupleSatisfiesMVCC
+ * True iff heap tuple is valid for the given MVCC snapshot.
+ *
+ * Here, we consider the effects of:
+ * all transactions committed as of the time of the given snapshot
+ * previous commands of this transaction
+ *
+ * Does _not_ include:
+ * transactions shown as in-progress by the snapshot
+ * transactions started after the snapshot was taken
+ * changes made by the current command
+ *
+ * Notice that here, we will not update the tuple status hint bits if the
+ * inserting/deleting transaction is still running according to our snapshot,
+ * even if in reality it's committed or aborted by now. This is intentional.
+ * Checking the true transaction state would require access to high-traffic
+ * shared data structures, creating contention we'd rather do without, and it
+ * would not change the result of our visibility check anyway. The hint bits
+ * will be updated by the first visitor that has a snapshot new enough to see
+ * the inserting/deleting transaction as done. In the meantime, the cost of
+ * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
+ * call will need to run TransactionIdIsCurrentTransactionId in addition to
+ * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
+ * coding where we tried to set the hint bits as soon as possible, we instead
+ * did TransactionIdIsInProgress in each call --- to no avail, as long as the
+ * inserting/deleting transaction was still running --- which was more cycles
+ * and more contention on the PGXACT array.
+ */
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return false;
+
+ /* Used by pre-9.0 binary upgrades */
+ if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return false;
+ if (!XidInMVCCSnapshot(xvac, snapshot))
+ {
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (!TransactionIdIsCurrentTransactionId(xvac))
+ {
+ if (XidInMVCCSnapshot(xvac, snapshot))
+ return false;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ return false; /* inserted after scan started */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* updating subtransaction must have aborted */
+ if (!TransactionIdIsCurrentTransactionId(xmax))
+ return true;
+ else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* updated after scan started */
+ else
+ return false; /* updated before scan started */
+ }
+
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* deleting subtransaction must have aborted */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return false;
+ }
+ }
+ else
+ {
+ /* xmin is committed, but maybe not according to our snapshot */
+ if (!HeapTupleHeaderXminFrozen(tuple) &&
+ XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ return false; /* treat as still in progress */
+ }
+
+ /* by here, the inserting transaction has committed */
+
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
+ return true;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ if (XidInMVCCSnapshot(xmax, snapshot))
+ return true;
+ if (TransactionIdDidCommit(xmax))
+ return false; /* updating transaction committed */
+ /* it must have aborted or crashed */
+ return true;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true;
+
+ if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ {
+ /* it must have aborted or crashed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return true;
+ }
+
+ /* xmax transaction committed */
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ }
+ else
+ {
+ /* xmax is committed, but maybe not according to our snapshot */
+ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ return true; /* treat as still in progress */
+ }
+
+ /* xmax transaction committed */
+
+ return false;
+}
+
+
+/*
+ * HeapTupleSatisfiesVacuum
+ *
+ * Determine the status of tuples for VACUUM purposes. Here, what
+ * we mainly want to know is if a tuple is potentially visible to *any*
+ * running transaction. If so, it can't be removed yet by VACUUM.
+ *
+ * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
+ * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ */
+static HTSV_Result
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * Has inserting transaction committed?
+ *
+ * If the inserting transaction aborted, then the tuple was never visible
+ * to any other transaction, so we can delete it immediately.
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ {
+ if (HeapTupleHeaderXminInvalid(tuple))
+ return HEAPTUPLE_DEAD;
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_OFF)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ }
+ /* Used by pre-9.0 binary upgrades */
+ else if (tuple->t_infomask & HEAP_MOVED_IN)
+ {
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+ if (TransactionIdIsCurrentTransactionId(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdIsInProgress(xvac))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ if (TransactionIdDidCommit(xvac))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ InvalidTransactionId);
+ else
+ {
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+ }
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* only locked? run infomask-only check first, for performance */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple))
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ /* inserted and then deleted by same xact */
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ /* deleting subtransaction must have aborted */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ {
+ /*
+ * It'd be possible to discern between INSERT/DELETE in progress
+ * here by looking at xmax - but that doesn't seem beneficial for
+ * the majority of callers and even detrimental for some. We'd
+ * rather have callers look at/wait for xmin than xmax. It's
+ * always correct to return INSERT_IN_PROGRESS because that's
+ * what's happening from the view of other backends.
+ */
+ return HEAPTUPLE_INSERT_IN_PROGRESS;
+ }
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+ HeapTupleHeaderGetRawXmin(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * At this point the xmin is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Okay, the inserter committed, so it was good at some point. Now what
+ * about the deleting transaction?
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return HEAPTUPLE_LIVE;
+
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ {
+ /*
+ * "Deleting" xact really only locked it, so the tuple is live in any
+ * case. However, we should make sure that either XMAX_COMMITTED or
+ * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
+ * examining the tuple for future xacts.
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ /*
+ * If it's a pre-pg_upgrade tuple, the multixact cannot
+ * possibly be running; otherwise have to check.
+ */
+ if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
+ MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ true))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ }
+ else
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_LIVE;
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ }
+ }
+
+ /*
+ * We don't really care whether xmax did commit, abort or crash. We
+ * know that xmax did lock the tuple, but it did not and will never
+ * actually update it.
+ */
+
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ TransactionId xmax;
+
+ if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ {
+ /* already checked above */
+ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsInProgress(xmax))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(xmax))
+ /* there are still lockers around -- can't return DEAD here */
+ return HEAPTUPLE_RECENTLY_DEAD;
+ /* updating transaction aborted */
+ return HEAPTUPLE_LIVE;
+ }
+
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
+
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ /* multi is not running -- updating xact cannot be */
+ Assert(!TransactionIdIsInProgress(xmax));
+ if (TransactionIdDidCommit(xmax))
+ {
+ if (!TransactionIdPrecedes(xmax, OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+ else
+ return HEAPTUPLE_DEAD;
+ }
+
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed.
+ * Remove the Xmax.
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ {
+ if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ return HEAPTUPLE_DELETE_IN_PROGRESS;
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
+ HeapTupleHeaderGetRawXmax(tuple));
+ else
+ {
+ /*
+ * Not in Progress, Not Committed, so either Aborted or crashed
+ */
+ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+ InvalidTransactionId);
+ return HEAPTUPLE_LIVE;
+ }
+
+ /*
+ * At this point the xmax is known committed, but we might not have
+ * been able to set the hint bit yet; so we can no longer Assert that
+ * it's set.
+ */
+ }
+
+ /*
+ * Deleter committed, but perhaps it was recent enough that some open
+ * transactions could still see the tuple.
+ */
+ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
+ return HEAPTUPLE_RECENTLY_DEAD;
+
+ /* Otherwise, it's dead and removable */
+ return HEAPTUPLE_DEAD;
+}
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
+ */
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
+ Buffer buffer)
+{
+ return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+ != HEAPTUPLE_DEAD;
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except when it's known the
+ * XID could not be ours anyway.
+ */
+bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
+ */
+static bool
+TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
+{
+ return bsearch(&xid, xip, num,
+ sizeof(TransactionId), xidComparator) != NULL;
+}
+
+/*
+ * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
+ * obeys.
+ *
+ * Only usable on tuples from catalog tables!
+ *
+ * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
+ * reading catalog pages which couldn't have been created in an older version.
+ *
+ * We don't set any hint bits in here as it seems unlikely to be beneficial as
+ * those should already be set by normal access and it seems to be too
+ * dangerous to do so as the semantics of doing so during timetravel are more
+ * complicated than when dealing "only" with the present.
+ */
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
+ Buffer buffer)
+{
+ HeapTuple htup = (HeapTuple) stup;
+ HeapTupleHeader tuple = htup->t_data;
+ TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /* inserting transaction aborted */
+ if (HeapTupleHeaderXminInvalid(tuple))
+ {
+ Assert(!TransactionIdDidCommit(xmin));
+ return false;
+ }
+ /* check if it's one of our txids, toplevel is also in there */
+ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
+ CommandId cmax = InvalidCommandId;
+
+ /*
+ * another transaction might have (tried to) delete this tuple or
+ * cmin/cmax was stored in a combocid. So we need to lookup the actual
+ * values externally.
+ */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
+
+ Assert(cmin != InvalidCommandId);
+
+ if (cmin >= snapshot->curcid)
+ return false; /* inserted after scan started */
+ /* fall through */
+ }
+ /* committed before our xmin horizon. Do a normal visibility check. */
+ else if (TransactionIdPrecedes(xmin, snapshot->xmin))
+ {
+ Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin)));
+
+ /* check for hint bit first, consult clog afterwards */
+ if (!HeapTupleHeaderXminCommitted(tuple) &&
+ !TransactionIdDidCommit(xmin))
+ return false;
+ /* fall through */
+ }
+ /* beyond our xmax horizon, i.e. invisible */
+ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
+ {
+ return false;
+ }
+ /* check if it's a committed transaction in [xmin, xmax) */
+ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ {
+ /* fall through */
+ }
+
+ /*
+ * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
+ * invisible.
+ */
+ else
+ {
+ return false;
+ }
+
+ /* at this point we know xmin is visible, go on to check xmax */
+
+ /* xid invalid or aborted */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+ /* locked tuples are always visible */
+ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return true;
+
+ /*
+ * We can see multis here if we're looking at user tables or if somebody
+ * SELECT ... FOR SHARE/UPDATE a system table.
+ */
+ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ xmax = HeapTupleGetUpdateXid(tuple);
+ }
+
+ /* check if it's one of our txids, toplevel is also in there */
+ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ {
+ bool resolved;
+ CommandId cmin;
+ CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
+
+ /* Lookup actual cmin/cmax values */
+ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
+ htup, buffer,
+ &cmin, &cmax);
+
+ if (!resolved)
+ elog(ERROR, "could not resolve combocid to cmax");
+
+ Assert(cmax != InvalidCommandId);
+
+ if (cmax >= snapshot->curcid)
+ return true; /* deleted after scan started */
+ else
+ return false; /* deleted before scan started */
+ }
+ /* below xmin horizon, normal transaction state is valid */
+ else if (TransactionIdPrecedes(xmax, snapshot->xmin))
+ {
+ Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
+ !TransactionIdDidCommit(xmax)));
+
+ /* check hint bit first */
+ if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
+ return false;
+
+ /* check clog */
+ return !TransactionIdDidCommit(xmax);
+ }
+ /* above xmax horizon, we cannot possibly see the deleting transaction */
+ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
+ return true;
+ /* xmax is between [xmin, xmax), check known committed array */
+ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ return false;
+ /* xmax is between [xmin, xmax), but known not to have committed yet */
+ else
+ return true;
+}
+
+static bool
+HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ switch (snapshot->visibility_type)
+ {
+ case MVCC_VISIBILITY:
+ return HeapTupleSatisfiesMVCC(stup, snapshot, buffer);
+ break;
+ case SELF_VISIBILITY:
+ return HeapTupleSatisfiesSelf(stup, snapshot, buffer);
+ break;
+ case ANY_VISIBILITY:
+ return HeapTupleSatisfiesAny(stup, snapshot, buffer);
+ break;
+ case TOAST_VISIBILITY:
+ return HeapTupleSatisfiesToast(stup, snapshot, buffer);
+ break;
+ case DIRTY_VISIBILITY:
+ return HeapTupleSatisfiesDirty(stup, snapshot, buffer);
+ break;
+ case HISTORIC_MVCC_VISIBILITY:
+ return HeapTupleSatisfiesHistoricMVCC(stup, snapshot, buffer);
+ break;
+ case NON_VACUUMABLE_VISIBILTY:
+ return HeapTupleSatisfiesNonVacuumable(stup, snapshot, buffer);
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies = HeapTupleSatisfies;
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 52231ac417..74250688a8 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194e18..3118c2d33f 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -108,6 +108,7 @@
#include "miscadmin.h"
#include "access/heapam.h"
+#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
#include "access/transam.h"
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da001a..01321a2543 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..17be9a4042 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2222,6 +2222,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2277,6 +2278,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2357,8 +2359,8 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
- scan->rs_cbuf))
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
+ scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead, we can ignore it */
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 760d19142e..247d9c2f56 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1083,9 +1083,9 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
- OldestXmin,
- targbuffer))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
+ OldestXmin,
+ targbuffer))
{
case HEAPTUPLE_LIVE:
sample_it = true;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e2ad..dbcc5bc172 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 6587db77ac..04729e5c27 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -355,7 +355,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
params->log_min_duration))
{
StringInfoData buf;
- char *msgfmt;
+ char *msgfmt;
TimestampDifference(starttime, endtime, &secs, &usecs);
@@ -986,7 +986,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2151,7 +2151,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index b885f2a3a6..3e22c6baf6 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -461,7 +461,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 0027d21253..dbc242c699 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -190,6 +190,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -201,7 +202,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -236,7 +237,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1312,7 +1313,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
@@ -1517,8 +1518,8 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
if (mtstate->mt_partition_dispatch_info != NULL)
{
/*
- * For tuple routing among partitions, we need TupleDescs based
- * on the partition routing table.
+ * For tuple routing among partitions, we need TupleDescs based on
+ * the partition routing table.
*/
ResultRelInfo **resultRelInfos = mtstate->mt_partitions;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a836e4..6a118d1883 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index ad65b9831d..86efe3a66b 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 251a359bff..4fbad9f0f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index ba0e3ad87d..16e6755844 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -289,7 +289,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa4c8..f17b1c5324 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index addf87dc3b..ec8bc82d6e 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2048,7 +2048,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2145,7 +2145,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
deleted file mode 100644
index b7aab0dd19..0000000000
--- a/src/backend/utils/time/tqual.c
+++ /dev/null
@@ -1,1807 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
- *
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted (and it is safe to set the hint bits).
- * If the hint bits are changed, MarkBufferDirtyHint is called on
- * the passed-in buffer. The caller must hold not only a pin, but at least
- * shared buffer content lock on the buffer containing the tuple.
- *
- * NOTE: When using a non-MVCC snapshot, we must check
- * TransactionIdIsInProgress (which looks in the PGXACT array)
- * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
- * pg_xact). Otherwise we have a race condition: we might decide that a
- * just-committed transaction crashed, because none of the tests succeed.
- * xact.c is careful to record commit/abort in pg_xact before it unsets
- * MyPgXact->xid in the PGXACT array. That fixes that problem, but it
- * also means there is a window where TransactionIdIsInProgress and
- * TransactionIdDidCommit will both return true. If we check only
- * TransactionIdDidCommit, we could consider a tuple committed when a
- * later GetSnapshotData call will still think the originating transaction
- * is in progress, which leads to application-level inconsistency. The
- * upshot is that we gotta check TransactionIdIsInProgress first in all
- * code paths, except for a few cases where we are looking at
- * subtransactions of our own main transaction and so there can't be any
- * race condition.
- *
- * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
- * TransactionIdIsInProgress, but the logic is otherwise the same: do not
- * check pg_xact until after deciding that the xact is no longer in progress.
- *
- *
- * Summary of visibility functions:
- *
- * HeapTupleSatisfiesMVCC()
- * visible to supplied snapshot, excludes current command
- * HeapTupleSatisfiesUpdate()
- * visible to instant snapshot, with user-supplied command
- * counter and more complex result
- * HeapTupleSatisfiesSelf()
- * visible to instant snapshot and current command
- * HeapTupleSatisfiesDirty()
- * like HeapTupleSatisfiesSelf(), but includes open transactions
- * HeapTupleSatisfiesVacuum()
- * visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
- * HeapTupleSatisfiesToast()
- * visible unless part of interrupted vacuum, used for TOAST
- * HeapTupleSatisfiesAny()
- * all tuples are visible
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
-/*
- * HeapTupleSatisfiesSelf
- * True iff heap tuple is valid "for itself".
- *
- * Here, we consider the effects of:
- * all committed transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * Note:
- * Assumes heap tuple is valid.
- *
- * The satisfaction of "itself" requires the following:
- *
- * ((Xmin == my-transaction && the row was updated by the current transaction, and
- * (Xmax is null it was not deleted
- * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
- * ||
- *
- * (Xmin is committed && the row was modified by a committed transaction, and
- * (Xmax is null || the row has not been deleted, or
- * (Xmax != my-transaction && the row was deleted by another transaction
- * Xmax is not committed))) that has not been committed
- */
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false;
-}
-
-/*
- * HeapTupleSatisfiesAny
- * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
- */
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
-{
- return true;
-}
-
-/*
- * HeapTupleSatisfiesToast
- * True iff heap tuple is valid as a TOAST row.
- *
- * This is a simplified version that only checks for VACUUM moving conditions.
- * It's appropriate for TOAST usage because TOAST really doesn't want to do
- * its own time qual checks; if you can see the main table row that contains
- * a TOAST reference, you should be able to see the TOASTed value. However,
- * vacuuming a TOAST table is independent of the main table, and in case such
- * a vacuum fails partway through, we'd better do this much checking.
- *
- * Among other things, this means you can't do UPDATEs of rows in a TOAST
- * table.
- */
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
-
- /*
- * An invalid Xmin can be left behind by a speculative insertion that
- * is canceled by super-deleting the tuple. This also applies to
- * TOAST tuples created during speculative insertion.
- */
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
- return false;
- }
-
- /* otherwise assume the tuple is valid for TOAST. */
- return true;
-}
-
-/*
- * HeapTupleSatisfiesUpdate
- *
- * This function returns a more detailed result code than most of the
- * functions in this file, since UPDATE needs to know more than "is it
- * visible?". It also allows for user-supplied CommandId rather than
- * relying on CurrentCommandId.
- *
- * The possible return codes are:
- *
- * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
- * e.g. it was created by a later CommandId.
- *
- * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
- * updated.
- *
- * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
- * after the current scan started.
- *
- * HeapTupleUpdated: The tuple was updated by a committed transaction.
- *
- * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
- * transaction other than the current transaction. (Note: this includes
- * the case where the tuple is share-locked by a MultiXact, even if the
- * MultiXact includes the current transaction. Callers that want to
- * distinguish that case must test for it themselves.)
- */
-HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HeapTupleInvisible;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HeapTupleInvisible;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return HeapTupleInvisible;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
- return HeapTupleInvisible; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- TransactionId xmax;
-
- xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- /*
- * Careful here: even though this tuple was created by our own
- * transaction, it might be locked by other transactions, if
- * the original version was key-share locked when we updated
- * it.
- */
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- if (MultiXactIdIsRunning(xmax, true))
- return HeapTupleBeingUpdated;
- else
- return HeapTupleMayBeUpdated;
- }
-
- /*
- * If the locker is gone, then there is nothing of interest
- * left in this Xmax; otherwise, report the tuple as
- * locked/updated.
- */
- if (!TransactionIdIsInProgress(xmax))
- return HeapTupleMayBeUpdated;
- return HeapTupleBeingUpdated;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* deleting subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- false))
- return HeapTupleBeingUpdated;
- return HeapTupleMayBeUpdated;
- }
- else
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan
- * started */
- else
- return HeapTupleInvisible; /* updated before scan
- * started */
- }
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HeapTupleInvisible;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return HeapTupleMayBeUpdated;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
- return HeapTupleUpdated; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
- return HeapTupleMayBeUpdated;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
- return HeapTupleBeingUpdated;
-
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- xmax = HeapTupleGetUpdateXid(tuple);
- if (!TransactionIdIsValid(xmax))
- {
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
- }
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- return HeapTupleBeingUpdated;
-
- if (TransactionIdDidCommit(xmax))
- return HeapTupleUpdated;
-
- /*
- * By here, the update in the Xmax is either aborted or crashed, but
- * what about the other members?
- */
-
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /*
- * There's no member, even just a locker, alive anymore, so we can
- * mark the Xmax as invalid.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
- else
- {
- /* There are lockers running */
- return HeapTupleBeingUpdated;
- }
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return HeapTupleBeingUpdated;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
- return HeapTupleSelfUpdated; /* updated after scan started */
- else
- return HeapTupleInvisible; /* updated before scan started */
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HeapTupleBeingUpdated;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HeapTupleMayBeUpdated;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return HeapTupleUpdated; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesDirty
- * True iff heap tuple is valid including effects of open transactions.
- *
- * Here, we consider the effects of:
- * all committed and in-progress transactions (as of the current instant)
- * previous commands of this transaction
- * changes made by the current command
- *
- * This is essentially like HeapTupleSatisfiesSelf as far as effects of
- * the current transaction and committed/aborted xacts are concerned.
- * However, we also include the effects of other xacts still in progress.
- *
- * A special hack is that the passed-in snapshot struct is used as an
- * output argument to return the xids of concurrent xacts that affected the
- * tuple. snapshot->xmin is set to the tuple's xmin if that is another
- * transaction that's still in progress; or to InvalidTransactionId if the
- * tuple's xmin is committed good, committed dead, or my own xact.
- * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
- * inserted speculatively, meaning that the inserter might still back down
- * on the insertion without aborting the whole transaction, the associated
- * token is also returned in snapshot->speculativeToken.
- */
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- snapshot->speculativeToken = 0;
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!TransactionIdIsInProgress(xvac))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (TransactionIdIsInProgress(xvac))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else
- return false;
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- return false;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * Return the speculative token to caller. Caller can worry about
- * xmax, since it requires a conclusively locked row version, and
- * a concurrent update to this tuple is a conflict of its
- * purposes.
- */
- if (HeapTupleHeaderIsSpeculative(tuple))
- {
- snapshot->speculativeToken =
- HeapTupleHeaderGetSpeculativeToken(tuple);
-
- Assert(snapshot->speculativeToken != 0);
- }
-
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
- /* XXX shouldn't we fall through to look at xmax? */
- return true; /* in insertion by other */
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false; /* updated by other */
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- {
- snapshot->xmax = xmax;
- return true;
- }
- if (TransactionIdDidCommit(xmax))
- return false;
- /* it must have aborted or crashed */
- return true;
- }
-
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
- return false;
- }
-
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
- return true;
- }
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- return false; /* updated by other */
-}
-
-/*
- * HeapTupleSatisfiesMVCC
- * True iff heap tuple is valid for the given MVCC snapshot.
- *
- * Here, we consider the effects of:
- * all transactions committed as of the time of the given snapshot
- * previous commands of this transaction
- *
- * Does _not_ include:
- * transactions shown as in-progress by the snapshot
- * transactions started after the snapshot was taken
- * changes made by the current command
- *
- * Notice that here, we will not update the tuple status hint bits if the
- * inserting/deleting transaction is still running according to our snapshot,
- * even if in reality it's committed or aborted by now. This is intentional.
- * Checking the true transaction state would require access to high-traffic
- * shared data structures, creating contention we'd rather do without, and it
- * would not change the result of our visibility check anyway. The hint bits
- * will be updated by the first visitor that has a snapshot new enough to see
- * the inserting/deleting transaction as done. In the meantime, the cost of
- * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
- * call will need to run TransactionIdIsCurrentTransactionId in addition to
- * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
- * coding where we tried to set the hint bits as soon as possible, we instead
- * did TransactionIdIsInProgress in each call --- to no avail, as long as the
- * inserting/deleting transaction was still running --- which was more cycles
- * and more contention on the PGXACT array.
- */
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return false;
-
- /* Used by pre-9.0 binary upgrades */
- if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return false;
- if (!XidInMVCCSnapshot(xvac, snapshot))
- {
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (!TransactionIdIsCurrentTransactionId(xvac))
- {
- if (XidInMVCCSnapshot(xvac, snapshot))
- return false;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
- return false; /* inserted after scan started */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* updating subtransaction must have aborted */
- if (!TransactionIdIsCurrentTransactionId(xmax))
- return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* updated after scan started */
- else
- return false; /* updated before scan started */
- }
-
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* deleting subtransaction must have aborted */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return false;
- }
- }
- else
- {
- /* xmin is committed, but maybe not according to our snapshot */
- if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
- return false; /* treat as still in progress */
- }
-
- /* by here, the inserting transaction has committed */
-
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
- return true;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- if (XidInMVCCSnapshot(xmax, snapshot))
- return true;
- if (TransactionIdDidCommit(xmax))
- return false; /* updating transaction committed */
- /* it must have aborted or crashed */
- return true;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
- {
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
-
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true;
-
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- {
- /* it must have aborted or crashed */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return true;
- }
-
- /* xmax transaction committed */
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- }
- else
- {
- /* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
- return true; /* treat as still in progress */
- }
-
- /* xmax transaction committed */
-
- return false;
-}
-
-
-/*
- * HeapTupleSatisfiesVacuum
- *
- * Determine the status of tuples for VACUUM purposes. Here, what
- * we mainly want to know is if a tuple is potentially visible to *any*
- * running transaction. If so, it can't be removed yet by VACUUM.
- *
- * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
- * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
- * still be visible to some open transaction, so we can't remove them,
- * even if we see that the deleting transaction has committed.
- */
-HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * Has inserting transaction committed?
- *
- * If the inserting transaction aborted, then the tuple was never visible
- * to any other transaction, so we can delete it immediately.
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- {
- if (HeapTupleHeaderXminInvalid(tuple))
- return HEAPTUPLE_DEAD;
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_OFF)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- }
- /* Used by pre-9.0 binary upgrades */
- else if (tuple->t_infomask & HEAP_MOVED_IN)
- {
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
-
- if (TransactionIdIsCurrentTransactionId(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdIsInProgress(xvac))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- if (TransactionIdDidCommit(xvac))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- InvalidTransactionId);
- else
- {
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
- }
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
- {
- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* only locked? run infomask-only check first, for performance */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- /* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- /* deleting subtransaction must have aborted */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
- {
- /*
- * It'd be possible to discern between INSERT/DELETE in progress
- * here by looking at xmax - but that doesn't seem beneficial for
- * the majority of callers and even detrimental for some. We'd
- * rather have callers look at/wait for xmin than xmax. It's
- * always correct to return INSERT_IN_PROGRESS because that's
- * what's happening from the view of other backends.
- */
- return HEAPTUPLE_INSERT_IN_PROGRESS;
- }
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * At this point the xmin is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Okay, the inserter committed, so it was good at some point. Now what
- * about the deleting transaction?
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return HEAPTUPLE_LIVE;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- {
- /*
- * "Deleting" xact really only locked it, so the tuple is live in any
- * case. However, we should make sure that either XMAX_COMMITTED or
- * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
- * examining the tuple for future xacts.
- */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- /*
- * If it's a pre-pg_upgrade tuple, the multixact cannot
- * possibly be running; otherwise have to check.
- */
- if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
- true))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- }
- else
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_LIVE;
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- }
- }
-
- /*
- * We don't really care whether xmax did commit, abort or crash. We
- * know that xmax did lock the tuple, but it did not and will never
- * actually update it.
- */
-
- return HEAPTUPLE_LIVE;
- }
-
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- TransactionId xmax;
-
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
- {
- /* already checked above */
- Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsInProgress(xmax))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(xmax))
- /* there are still lockers around -- can't return DEAD here */
- return HEAPTUPLE_RECENTLY_DEAD;
- /* updating transaction aborted */
- return HEAPTUPLE_LIVE;
- }
-
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED));
-
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- /* multi is not running -- updating xact cannot be */
- Assert(!TransactionIdIsInProgress(xmax));
- if (TransactionIdDidCommit(xmax))
- {
- if (!TransactionIdPrecedes(xmax, OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
- else
- return HEAPTUPLE_DEAD;
- }
-
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed.
- * Remove the Xmax.
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- {
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
- return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
- SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
- else
- {
- /*
- * Not in Progress, Not Committed, so either Aborted or crashed
- */
- SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
- InvalidTransactionId);
- return HEAPTUPLE_LIVE;
- }
-
- /*
- * At this point the xmax is known committed, but we might not have
- * been able to set the hint bit yet; so we can no longer Assert that
- * it's set.
- */
- }
-
- /*
- * Deleter committed, but perhaps it was recent enough that some open
- * transactions could still see the tuple.
- */
- if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin))
- return HEAPTUPLE_RECENTLY_DEAD;
-
- /* Otherwise, it's dead and removable */
- return HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleSatisfiesNonVacuumable
- *
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
- *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
- */
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
- != HEAPTUPLE_DEAD;
-}
-
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return FALSE when in doubt, but we must return TRUE only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except when it's known the
- * XID could not be ours anyway.
- */
-bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
-/*
- * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
- */
-static bool
-TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
-{
- return bsearch(&xid, xip, num,
- sizeof(TransactionId), xidComparator) != NULL;
-}
-
-/*
- * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
- * obeys.
- *
- * Only usable on tuples from catalog tables!
- *
- * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
- * reading catalog pages which couldn't have been created in an older version.
- *
- * We don't set any hint bits in here as it seems unlikely to be beneficial as
- * those should already be set by normal access and it seems to be too
- * dangerous to do so as the semantics of doing so during timetravel are more
- * complicated than when dealing "only" with the present.
- */
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
- Buffer buffer)
-{
- HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /* inserting transaction aborted */
- if (HeapTupleHeaderXminInvalid(tuple))
- {
- Assert(!TransactionIdDidCommit(xmin));
- return false;
- }
- /* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
- CommandId cmax = InvalidCommandId;
-
- /*
- * another transaction might have (tried to) delete this tuple or
- * cmin/cmax was stored in a combocid. So we need to lookup the actual
- * values externally.
- */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve cmin/cmax of catalog tuple");
-
- Assert(cmin != InvalidCommandId);
-
- if (cmin >= snapshot->curcid)
- return false; /* inserted after scan started */
- /* fall through */
- }
- /* committed before our xmin horizon. Do a normal visibility check. */
- else if (TransactionIdPrecedes(xmin, snapshot->xmin))
- {
- Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin)));
-
- /* check for hint bit first, consult clog afterwards */
- if (!HeapTupleHeaderXminCommitted(tuple) &&
- !TransactionIdDidCommit(xmin))
- return false;
- /* fall through */
- }
- /* beyond our xmax horizon, i.e. invisible */
- else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
- {
- return false;
- }
- /* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
- {
- /* fall through */
- }
-
- /*
- * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
- * invisible.
- */
- else
- {
- return false;
- }
-
- /* at this point we know xmin is visible, go on to check xmax */
-
- /* xid invalid or aborted */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
- /* locked tuples are always visible */
- else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return true;
-
- /*
- * We can see multis here if we're looking at user tables or if somebody
- * SELECT ... FOR SHARE/UPDATE a system table.
- */
- else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- {
- xmax = HeapTupleGetUpdateXid(tuple);
- }
-
- /* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
- {
- bool resolved;
- CommandId cmin;
- CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple);
-
- /* Lookup actual cmin/cmax values */
- resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
- htup, buffer,
- &cmin, &cmax);
-
- if (!resolved)
- elog(ERROR, "could not resolve combocid to cmax");
-
- Assert(cmax != InvalidCommandId);
-
- if (cmax >= snapshot->curcid)
- return true; /* deleted after scan started */
- else
- return false; /* deleted before scan started */
- }
- /* below xmin horizon, normal transaction state is valid */
- else if (TransactionIdPrecedes(xmax, snapshot->xmin))
- {
- Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
- !TransactionIdDidCommit(xmax)));
-
- /* check hint bit first */
- if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
- return false;
-
- /* check clog */
- return !TransactionIdDidCommit(xmax);
- }
- /* above xmax horizon, we cannot possibly see the deleting transaction */
- else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
- return true;
- /* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
- return false;
- /* xmax is between [xmin, xmax), but known not to have committed yet */
- else
- return true;
-}
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
new file mode 100644
index 0000000000..ff63cf3615
--- /dev/null
+++ b/src/include/access/heapam_common.h
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_shared.h
+ * POSTGRES heap access method definitions shared across
+ * server and heapam methods.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/heapam_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAPAM_SHARED_H
+#define HEAPAM_SHARED_H
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "nodes/lockoptions.h"
+#include "nodes/primnodes.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "storage/lockdefs.h"
+#include "storage/lmgr.h"
+#include "utils/relcache.h"
+#include "utils/snapshot.h"
+
+
+/* in heap/heapam_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* HEAPAM_H */
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 6459435c78..a5268242d7 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -28,15 +28,16 @@ typedef MinimalTupleData *MinimalTuple;
typedef enum tuple_visibility_type
{
- MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
- SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
END_OF_VISIBILITY
-} tuple_visibility_type;
+} tuple_visibility_type;
/* Result codes for HeapTupleSatisfiesVacuum */
typedef enum
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc5ba..b1c94de683 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,13 +14,13 @@
#ifndef BUFMGR_H
#define BUFMGR_H
+#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +268,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf519778df..5752ee4731 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -52,7 +52,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 4fda7f8384..3deeb71970 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,64 +43,25 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies) (tuple, snapshot, buffer))
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
-
-/*
- * To avoid leaking too much knowledge about reorderbuffer implementation
- * details this is implemented in reorderbuffer.c not tqual.c.
- */
-struct HTAB;
-extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
- HeapTuple htup,
- Buffer buffer,
- CommandId *cmin, CommandId *cmax);
-
/*
* We don't provide a static SnapshotDirty variable because it would be
* non-reentrant. Instead, users of that snapshot type should declare a
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -107,8 +69,19 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
+/*
+ * To avoid leaking too much knowledge about reorderbuffer implementation
+ * details this is implemented in reorderbuffer.c not tqual.c.
+ */
+struct HTAB;
+extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
+ Snapshot snapshot,
+ HeapTuple htup,
+ Buffer buffer,
+ CommandId *cmin, CommandId *cmax);
+
#endif /* TQUAL_H */
--
2.14.2.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (62.7K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/9-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 635ad38523e294cb5a4b659d8032149f784df9e0 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Fri, 27 Oct 2017 16:02:46 +1100
Subject: [PATCH 5/8] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +----------------------
src/backend/access/heap/heapam_common.c | 404 +++++++++++++++++++++++++++++++
src/backend/access/heap/heapam_storage.c | 5 +-
src/backend/commands/copy.c | 8 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 25 +-
src/backend/executor/execExprInterp.c | 35 +--
src/backend/executor/execReplication.c | 90 ++-----
src/backend/executor/execTuples.c | 268 +++++++++-----------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/heapam_common.h | 2 +
src/include/access/htup_details.h | 15 +-
src/include/executor/tuptable.h | 54 +++--
17 files changed, 660 insertions(+), 585 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 13ee528e26..aabb4f0944 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1021,111 +1022,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
}
-/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 4cc3eeff0c..6ebe0b290c 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -100,6 +100,410 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
return true;
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *) palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple) tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple) tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine *
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
/*
* HeapTupleIsSurelyDead
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index f7e11d4946..a874e243c9 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -1108,7 +1108,6 @@ HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
return false;
}
-
/*
* HeapTupleSatisfiesVacuum
*
@@ -1695,8 +1694,10 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
amroutine->snapshot_satisfies = HeapTupleSatisfies;
-
amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
+
+ amroutine->slot_storageam = heapam_storage_slot_handler;
+
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 1bdd4927d9..96cda7091b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -167,7 +167,7 @@ typedef struct CopyStateData
PartitionDispatch *partition_dispatch_info;
int num_dispatch; /* Number of entries in the above array */
int num_partitions; /* Number of members in the following arrays */
- ResultRelInfo **partitions; /* Per partition result relation pointers */
+ ResultRelInfo **partitions; /* Per partition result relation pointers */
TupleConversionMap **partition_tupconv_maps;
TupleTableSlot *partition_tuple_slot;
TransitionCaptureState *transition_capture;
@@ -359,7 +359,7 @@ SendCopyBegin(CopyState cstate)
pq_sendbyte(&buf, format); /* overall format */
pq_sendint16(&buf, natts);
for (i = 0; i < natts; i++)
- pq_sendint16(&buf, format); /* per-column formats */
+ pq_sendint16(&buf, format); /* per-column formats */
pq_endmessage(&buf);
cstate->copy_dest = COPY_NEW_FE;
}
@@ -392,7 +392,7 @@ ReceiveCopyBegin(CopyState cstate)
pq_sendbyte(&buf, format); /* overall format */
pq_sendint16(&buf, natts);
for (i = 0; i < natts; i++)
- pq_sendint16(&buf, format); /* per-column formats */
+ pq_sendint16(&buf, format); /* per-column formats */
pq_endmessage(&buf);
cstate->copy_dest = COPY_NEW_FE;
cstate->fe_msgbuf = makeStringInfo();
@@ -2717,7 +2717,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..213a8cccbc 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376511..b440740e28 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8d0345cd64..005bdbd023 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2289,7 +2289,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2370,7 +2370,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2728,7 +2728,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2770,7 +2770,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2879,7 +2879,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3608,9 +3608,9 @@ static void AfterTriggerExecute(AfterTriggerEvent event,
MemoryContext per_tuple_context,
TupleTableSlot *trig_tuple_slot1,
TupleTableSlot *trig_tuple_slot2);
-static AfterTriggersTableData *GetAfterTriggersTableData(Oid relid,
- CmdType cmdType);
-static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs);
+static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid,
+ CmdType cmdType);
+static void AfterTriggerFreeQuery(AfterTriggersQueryData * qs);
static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,
@@ -3882,7 +3882,7 @@ afterTriggerRestoreEventList(AfterTriggerEventList *events,
* ----------
*/
static void
-afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
+afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData * qs)
{
AfterTriggerEventChunk *target = qs->events.head;
ListCell *lc;
@@ -4006,14 +4006,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
@@ -4634,7 +4633,7 @@ AfterTriggerEndQuery(EState *estate)
* and then called again for the same query level.
*/
static void
-AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
+AfterTriggerFreeQuery(AfterTriggersQueryData * qs)
{
Tuplestorestate *ts;
List *tables;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a0f537b706..a7d20bc391 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -508,13 +508,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(innerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -526,13 +528,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+ Assert(outerslot->tts_storage != NULL);
+ /*
+ * hari
+ * Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(outerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -544,13 +547,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(scanslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fb538c0297..07640a9992 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,6 +226,7 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
+ TupleTableSlot *scanslot;
HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d166..93aeb3eb6e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -81,6 +81,7 @@
*/
#include "postgres.h"
+#include "access/heapam_common.h"
#include "access/htup_details.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -205,6 +205,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
return slot;
}
+/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert(slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,19 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +460,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +541,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +550,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +569,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +599,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +646,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +654,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +665,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +698,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +718,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,18 +729,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
@@ -768,17 +742,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6d5f..02f6c816aa 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index dbc242c699..ec5c543bdd 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -171,7 +171,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -271,7 +271,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -406,7 +406,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -419,7 +419,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -437,7 +437,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -746,7 +746,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -897,7 +897,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -958,7 +958,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -977,7 +977,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -991,7 +991,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1007,7 +1007,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1123,7 +1123,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index e9a5d5a1a5..9a47276274 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -58,7 +58,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
shm_mq_result result;
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 0e68670767..1e4f9be0d7 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -727,9 +727,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index ff63cf3615..feb35c5024 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -40,6 +40,8 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
+extern StorageSlotAmRoutine * heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b0d4c54121..168edb058d 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -20,6 +20,19 @@
#include "access/transam.h"
#include "storage/bufpage.h"
+/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
@@ -658,7 +671,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce4ee..a730f265dd 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.14.2.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (259.9K, ../../CAJrrPGey=taVXMjx4HBVV7vE0gGG4j+YrB7TZd-kh1GCb70=5Q@mail.gmail.com/10-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From c575e7ae8bc8d3e563b03e76f611273ce70de212 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Tue, 7 Nov 2017 12:06:12 +1100
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM. Move the index insertion
logic into storage AM, The Index insert still outside for
the case of multi_insert (Yet to change).
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 2859 ++++--------------------------
src/backend/access/heap/heapam_common.c | 2 +
src/backend/access/heap/heapam_storage.c | 2203 ++++++++++++++++++++++-
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/storageam.c | 281 +++
src/backend/access/heap/tuptoaster.c | 9 +-
src/backend/commands/copy.c | 39 +-
src/backend/commands/createas.c | 24 +-
src/backend/commands/matview.c | 22 +-
src/backend/commands/tablecmds.c | 6 +-
src/backend/commands/trigger.c | 49 +-
src/backend/executor/execIndexing.c | 2 +-
src/backend/executor/execMain.c | 130 +-
src/backend/executor/execReplication.c | 72 +-
src/backend/executor/execTuples.c | 1 +
src/backend/executor/nodeLockRows.c | 47 +-
src/backend/executor/nodeModifyTable.c | 244 ++-
src/backend/executor/nodeTidscan.c | 23 +-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 26 +-
src/include/access/heapam_common.h | 127 ++
src/include/access/htup_details.h | 1 +
src/include/access/storageam.h | 86 +
src/include/access/storageamapi.h | 34 +-
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 15 +-
src/include/executor/tuptable.h | 1 +
src/include/nodes/execnodes.h | 6 +-
src/include/storage/bufmgr.h | 1 -
31 files changed, 3457 insertions(+), 2891 deletions(-)
create mode 100644 src/backend/access/heap/storageam.c
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index aabb4f0944..a66eed8d3b 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
return PointerGetDatum(td);
}
+/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index e6bc18e5ea..162736ff15 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = heapam.o heapam_common.o heapam_storage.o hio.o \
- pruneheap.o rewriteheap.o storageamapi.o \
+ pruneheap.o rewriteheap.o storageam.o storageamapi.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index f62f04e185..d91452963a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -94,8 +94,6 @@ static HeapScanDesc heap_beginscan_internal(Relation relation,
bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
-static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tup,
@@ -103,108 +101,17 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
static Bitmapset *HeapDetermineModifiedColumns(Relation relation,
Bitmapset *interesting_cols,
HeapTuple oldtup, HeapTuple newtup);
-static bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
- LockTupleMode mode, LockWaitPolicy wait_policy,
- bool *have_tuple_lock);
-static void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2);
-static HTSU_Result heap_lock_updated_tuple(Relation rel, HeapTuple tuple,
- ItemPointer ctid, TransactionId xid,
- LockTupleMode mode);
static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
uint16 *new_infomask2);
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
uint16 t_infomask);
-static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
- LockTupleMode lockmode);
-static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
- Relation rel, ItemPointer ctid, XLTW_Oper oper,
- int *remaining);
-static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
- uint16 infomask, Relation rel, int *remaining);
-static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, bool nowait,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
bool *copy);
-
-/*
- * Each tuple lock mode has a corresponding heavyweight lock, and one or two
- * corresponding MultiXactStatuses (one to merely lock tuples, another one to
- * update them). This table (and the macros below) helps us determine the
- * heavyweight lock mode and MultiXactStatus values to use for any particular
- * tuple lock strength.
- *
- * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
- * instead.
- */
-static const struct
-{
- LOCKMODE hwlock;
- int lockstatus;
- int updstatus;
-}
-
- tupleLockExtraInfo[MaxLockTupleMode + 1] =
-{
- { /* LockTupleKeyShare */
- AccessShareLock,
- MultiXactStatusForKeyShare,
- -1 /* KeyShare does not allow updating tuples */
- },
- { /* LockTupleShare */
- RowShareLock,
- MultiXactStatusForShare,
- -1 /* Share does not allow updating tuples */
- },
- { /* LockTupleNoKeyExclusive */
- ExclusiveLock,
- MultiXactStatusForNoKeyUpdate,
- MultiXactStatusNoKeyUpdate
- },
- { /* LockTupleExclusive */
- AccessExclusiveLock,
- MultiXactStatusForUpdate,
- MultiXactStatusUpdate
- }
-};
-
-/* Get the LOCKMODE for a given MultiXactStatus */
-#define LOCKMODE_from_mxstatus(status) \
- (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
-
-/*
- * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
- * This is more readable than having every caller translate it to lock.h's
- * LOCKMODE.
- */
-#define LockTupleTuplock(rel, tup, mode) \
- LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define UnlockTupleTuplock(rel, tup, mode) \
- UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-#define ConditionalLockTupleTuplock(rel, tup, mode) \
- ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
-
-/*
- * This table maps tuple lock strength values for each particular
- * MultiXactStatus value.
- */
-static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
-{
- LockTupleKeyShare, /* ForKeyShare */
- LockTupleShare, /* ForShare */
- LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
- LockTupleExclusive, /* ForUpdate */
- LockTupleNoKeyExclusive, /* NoKeyUpdate */
- LockTupleExclusive /* Update */
-};
-
-/* Get the LockTupleMode for a given MultiXactStatus */
-#define TUPLOCK_from_mxstatus(status) \
- (MultiXactStatusLock[(status)])
-
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -1854,158 +1761,6 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
return heap_copytuple(&(scan->rs_ctup));
}
-/*
- * heap_fetch - retrieve tuple with given tid
- *
- * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
- * the tuple, fill in the remaining fields of *tuple, and check the tuple
- * against the specified snapshot.
- *
- * If successful (tuple found and passes snapshot time qual), then *userbuf
- * is set to the buffer holding the tuple and TRUE is returned. The caller
- * must unpin the buffer when done with the tuple.
- *
- * If the tuple is not found (ie, item number references a deleted slot),
- * then tuple->t_data is set to NULL and FALSE is returned.
- *
- * If the tuple is found but fails the time qual check, then FALSE is returned
- * but tuple->t_data is left pointing to the tuple.
- *
- * keep_buf determines what is done with the buffer in the FALSE-result cases.
- * When the caller specifies keep_buf = true, we retain the pin on the buffer
- * and return it in *userbuf (so the caller must eventually unpin it); when
- * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
- *
- * stats_relation is the relation to charge the heap_fetch operation against
- * for statistical purposes. (This could be the heap rel itself, an
- * associated index, or NULL to not count the fetch at all.)
- *
- * heap_fetch does not follow HOT chains: only the exact TID requested will
- * be fetched.
- *
- * It is somewhat inconsistent that we ereport() on invalid block number but
- * return false on invalid item number. There are a couple of reasons though.
- * One is that the caller can relatively easily check the block number for
- * validity, but cannot check the item number without reading the page
- * himself. Another is that when we are following a t_ctid link, we can be
- * reasonably confident that the page number is valid (since VACUUM shouldn't
- * truncate off the destination page without having killed the referencing
- * tuple first), but the item number might well not be good.
- */
-bool
-heap_fetch(Relation relation,
- Snapshot snapshot,
- HeapTuple tuple,
- Buffer *userbuf,
- bool keep_buf,
- Relation stats_relation)
-{
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- bool valid;
-
- /*
- * Fetch and pin the appropriate page of the relation.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
-
- /*
- * Need share lock on buffer to examine tuple commit status.
- */
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * We'd better check for out-of-range offnum in case of VACUUM since the
- * TID was obtained.
- */
- offnum = ItemPointerGetOffsetNumber(tid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * get the item line pointer corresponding to the requested tid
- */
- lp = PageGetItemId(page, offnum);
-
- /*
- * Must check for deleted tuple.
- */
- if (!ItemIdIsNormal(lp))
- {
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
- tuple->t_data = NULL;
- return false;
- }
-
- /*
- * fill in *tuple fields
- */
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
-
- /*
- * check time qualification of tuple, then release lock
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
-
- if (valid)
- PredicateLockTuple(relation, tuple, snapshot);
-
- CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
-
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-
- if (valid)
- {
- /*
- * All checks passed, so return the tuple as valid. Caller is now
- * responsible for releasing the buffer.
- */
- *userbuf = buffer;
-
- /* Count the successful fetch against appropriate rel, if any */
- if (stats_relation != NULL)
- pgstat_count_heap_fetch(stats_relation);
-
- return true;
- }
-
- /* Tuple failed time qual, but maybe caller wants to see it anyway. */
- if (keep_buf)
- *userbuf = buffer;
- else
- {
- ReleaseBuffer(buffer);
- *userbuf = InvalidBuffer;
- }
-
- return false;
-}
-
/*
* heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
*
@@ -2189,131 +1944,6 @@ heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
return result;
}
-/*
- * heap_get_latest_tid - get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to
- * the passed snapshot. You can pass SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row. Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(Relation relation,
- Snapshot snapshot,
- ItemPointer tid)
-{
- BlockNumber blk;
- ItemPointerData ctid;
- TransactionId priorXmax;
-
- /* this is to avoid Assert failures on bad input */
- if (!ItemPointerIsValid(tid))
- return;
-
- /*
- * Since this can be called with user-supplied TID, don't trust the input
- * too much. (RelationGetNumberOfBlocks is an expensive check, so we
- * don't check t_ctid links again this way. Note that it would not do to
- * call it just once and save the result, either.)
- */
- blk = ItemPointerGetBlockNumber(tid);
- if (blk >= RelationGetNumberOfBlocks(relation))
- elog(ERROR, "block number %u is out of range for relation \"%s\"",
- blk, RelationGetRelationName(relation));
-
- /*
- * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
- * need to examine, and *tid is the TID we will return if ctid turns out
- * to be bogus.
- *
- * Note that we will loop until we reach the end of the t_ctid chain.
- * Depending on the snapshot passed, there might be at most one visible
- * version of the row, but we don't try to optimize for that.
- */
- ctid = *tid;
- priorXmax = InvalidTransactionId; /* cannot check first XMIN */
- for (;;)
- {
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp;
- HeapTupleData tp;
- bool valid;
-
- /*
- * Read, pin, and lock the page.
- */
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, relation, page);
-
- /*
- * Check for bogus item number. This is not treated as an error
- * condition because it can happen while following a t_ctid link. We
- * just assume that the prior tid is OK and return it unchanged.
- */
- offnum = ItemPointerGetOffsetNumber(&ctid);
- if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
- lp = PageGetItemId(page, offnum);
- if (!ItemIdIsNormal(lp))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /* OK to access the tuple */
- tp.t_self = ctid;
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_tableOid = RelationGetRelid(relation);
-
- /*
- * After following a t_ctid link, we might arrive at an unrelated
- * tuple. Check for XMIN match.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Check time qualification of tuple; if visible, set it as the new
- * result candidate.
- */
- valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
- CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
- if (valid)
- *tid = ctid;
-
- /*
- * If there's a valid t_ctid link, follow it, else we're done.
- */
- if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
- ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
- {
- UnlockReleaseBuffer(buffer);
- break;
- }
-
- ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
- UnlockReleaseBuffer(buffer);
- } /* end of loop */
-}
-
-
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
*
@@ -2330,7 +1960,7 @@ heap_get_latest_tid(Relation relation,
*
* Note this is not allowed for tuples whose xmax is a multixact.
*/
-static void
+void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
@@ -2386,7 +2016,6 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate)
bistate->current_buf = InvalidBuffer;
}
-
/*
* heap_insert - insert tuple into a heap
*
@@ -2613,7 +2242,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* tuple if not. Note that in any case, the header fields are also set in
* the original tuple.
*/
-static HeapTuple
+HeapTuple
heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
CommandId cid, int options)
{
@@ -2683,406 +2312,104 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
}
/*
- * heap_multi_insert - insert multiple tuple into a heap
+ * simple_heap_insert - insert a tuple
+ *
+ * Currently, this routine differs from heap_insert only in supplying
+ * a default command ID and not allowing access to the speedup options.
*
- * This is like heap_insert(), but inserts multiple tuples in one operation.
- * That's faster than calling heap_insert() in a loop, because when multiple
- * tuples can be inserted on a single page, we can write just a single WAL
- * record covering all of them, and only need to lock/unlock the page once.
+ * This should be used rather than using heap_insert directly in most places
+ * where we are modifying system catalogs.
+ */
+Oid
+simple_heap_insert(Relation relation, HeapTuple tup)
+{
+ return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
+}
+
+/*
+ * Given infomask/infomask2, compute the bits that must be saved in the
+ * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
+ * xl_heap_lock_updated WAL records.
*
- * Note: this leaks memory into the current memory context. You can create a
- * temporary context before calling this, if that's a problem.
+ * See fix_infomask_from_infobits.
*/
-void
-heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate)
+uint8
+compute_infobits(uint16 infomask, uint16 infomask2)
+{
+ return
+ ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
+ ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
+ ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
+ /* note we ignore HEAP_XMAX_SHR_LOCK here */
+ ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
+ ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
+ XLHL_KEYS_UPDATED : 0);
+}
+
+
+
+/*
+ * heap_delete - delete a tuple
+ *
+ * NB: do not call this directly unless you are prepared to deal with
+ * concurrent-update conditions. Use simple_heap_delete instead.
+ *
+ * relation - table to be modified (caller must hold suitable lock)
+ * tid - TID of tuple to be deleted
+ * cid - delete command ID (used for visibility test, and stored into
+ * cmax if successful)
+ * crosscheck - if not InvalidSnapshot, also check tuple against this
+ * wait - true if should wait for any conflicting update to commit/abort
+ * hufd - output parameter, filled in failure cases (see below)
+ *
+ * Normal, successful return value is HeapTupleMayBeUpdated, which
+ * actually means we did delete it. Failure return codes are
+ * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
+ * (the last only possible if wait == false).
+ *
+ * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
+ * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
+ * (the last only for HeapTupleSelfUpdated, since we
+ * cannot obtain cmax from a combocid generated by another transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ */
+HTSU_Result
+heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
{
+ HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
- HeapTuple *heaptuples;
- int i;
- int ndone;
- char *scratch = NULL;
+ ItemId lp;
+ HeapTupleData tp;
Page page;
- bool needwal;
- Size saveFreeSpace;
- bool need_tuple_data = RelationIsLogicallyLogged(relation);
- bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
-
- needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ BlockNumber block;
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
+ bool have_tuple_lock = false;
+ bool iscombo;
+ bool all_visible_cleared = false;
+ HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
+ bool old_key_copied = false;
- /* Toast and set header data in all the tuples */
- heaptuples = palloc(ntuples * sizeof(HeapTuple));
- for (i = 0; i < ntuples; i++)
- heaptuples[i] = heap_prepare_insert(relation, tuples[i],
- xid, cid, options);
+ Assert(ItemPointerIsValid(tid));
/*
- * Allocate some memory to use for constructing the WAL record. Using
- * palloc() within a critical section is not safe, so we allocate this
- * beforehand.
+ * Forbid this during a parallel operation, lest it allocate a combocid.
+ * Other workers might need that combocid for visibility checks, and we
+ * have no provision for broadcasting it to them.
*/
- if (needwal)
- scratch = palloc(BLCKSZ);
+ if (IsInParallelMode())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+ errmsg("cannot delete tuples during a parallel operation")));
- /*
- * We're about to do the actual inserts -- but check for conflict first,
- * to minimize the possibility of having to roll back work we've just
- * done.
- *
- * A check here does not definitively prevent a serialization anomaly;
- * that check MUST be done at least past the point of acquiring an
- * exclusive buffer content lock on every buffer that will be affected,
- * and MAY be done after all inserts are reflected in the buffers and
- * those locks are released; otherwise there race condition. Since
- * multiple buffers can be locked and unlocked in the loop below, and it
- * would not be feasible to identify and lock all of those buffers before
- * the loop, we must do a final check at the end.
- *
- * The check here could be omitted with no loss of correctness; it is
- * present strictly as an optimization.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call, which makes for a faster check.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- ndone = 0;
- while (ndone < ntuples)
- {
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- bool all_visible_cleared = false;
- int nthispage;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Find buffer where at least the next tuple will fit. If the page is
- * all-visible, this will also pin the requisite visibility map page.
- */
- buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
- InvalidBuffer, options, bistate,
- &vmbuffer, NULL);
- page = BufferGetPage(buffer);
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- /*
- * RelationGetBufferForTuple has ensured that the first tuple fits.
- * Put that on the page, and then as many other tuples as fit.
- */
- RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
- for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
- {
- HeapTuple heaptup = heaptuples[ndone + nthispage];
-
- if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
- break;
-
- RelationPutHeapTuple(relation, buffer, heaptup, false);
-
- /*
- * We don't use heap_multi_insert for catalog tuples yet, but
- * better be prepared...
- */
- if (needwal && need_cids)
- log_heap_new_cid(relation, heaptup);
- }
-
- if (PageIsAllVisible(page))
- {
- all_visible_cleared = true;
- PageClearAllVisible(page);
- visibilitymap_clear(relation,
- BufferGetBlockNumber(buffer),
- vmbuffer, VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * XXX Should we set PageSetPrunable on this page ? See heap_insert()
- */
-
- MarkBufferDirty(buffer);
-
- /* XLOG stuff */
- if (needwal)
- {
- XLogRecPtr recptr;
- xl_heap_multi_insert *xlrec;
- uint8 info = XLOG_HEAP2_MULTI_INSERT;
- char *tupledata;
- int totaldatalen;
- char *scratchptr = scratch;
- bool init;
- int bufflags = 0;
-
- /*
- * If the page was previously empty, we can reinit the page
- * instead of restoring the whole thing.
- */
- init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
- PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
-
- /* allocate xl_heap_multi_insert struct from the scratch area */
- xlrec = (xl_heap_multi_insert *) scratchptr;
- scratchptr += SizeOfHeapMultiInsert;
-
- /*
- * Allocate offsets array. Unless we're reinitializing the page,
- * in that case the tuples are stored in order starting at
- * FirstOffsetNumber and we don't need to store the offsets
- * explicitly.
- */
- if (!init)
- scratchptr += nthispage * sizeof(OffsetNumber);
-
- /* the rest of the scratch space is used for tuple data */
- tupledata = scratchptr;
-
- xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
- xlrec->ntuples = nthispage;
-
- /*
- * Write out an xl_multi_insert_tuple and the tuple data itself
- * for each tuple.
- */
- for (i = 0; i < nthispage; i++)
- {
- HeapTuple heaptup = heaptuples[ndone + i];
- xl_multi_insert_tuple *tuphdr;
- int datalen;
-
- if (!init)
- xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
- /* xl_multi_insert_tuple needs two-byte alignment. */
- tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
- scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
-
- tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
- tuphdr->t_infomask = heaptup->t_data->t_infomask;
- tuphdr->t_hoff = heaptup->t_data->t_hoff;
-
- /* write bitmap [+ padding] [+ oid] + data */
- datalen = heaptup->t_len - SizeofHeapTupleHeader;
- memcpy(scratchptr,
- (char *) heaptup->t_data + SizeofHeapTupleHeader,
- datalen);
- tuphdr->datalen = datalen;
- scratchptr += datalen;
- }
- totaldatalen = scratchptr - tupledata;
- Assert((scratchptr - scratch) < BLCKSZ);
-
- if (need_tuple_data)
- xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
-
- /*
- * Signal that this is the last xl_heap_multi_insert record
- * emitted by this call to heap_multi_insert(). Needed for logical
- * decoding so it knows when to cleanup temporary data.
- */
- if (ndone + nthispage == ntuples)
- xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
-
- if (init)
- {
- info |= XLOG_HEAP_INIT_PAGE;
- bufflags |= REGBUF_WILL_INIT;
- }
-
- /*
- * If we're doing logical decoding, include the new tuple data
- * even if we take a full-page image of the page.
- */
- if (need_tuple_data)
- bufflags |= REGBUF_KEEP_DATA;
-
- XLogBeginInsert();
- XLogRegisterData((char *) xlrec, tupledata - scratch);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
-
- XLogRegisterBufData(0, tupledata, totaldatalen);
-
- /* filtering by origin on a row level is much more efficient */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- recptr = XLogInsert(RM_HEAP2_ID, info);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- ndone += nthispage;
- }
-
- /*
- * We're done with the actual inserts. Check for conflicts again, to
- * ensure that all rw-conflicts in to these inserts are detected. Without
- * this final check, a sequential scan of the heap may have locked the
- * table after the "before" check, missing one opportunity to detect the
- * conflict, and then scanned the table before the new tuples were there,
- * missing the other chance to detect the conflict.
- *
- * For heap inserts, we only need to check for table-level SSI locks. Our
- * new tuples can't possibly conflict with existing tuple locks, and heap
- * page locks are only consolidated versions of tuple locks; they do not
- * lock "gaps" as index page locks do. So we don't need to specify a
- * buffer when making the call.
- */
- CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
-
- /*
- * If tuples are cachable, mark them for invalidation from the caches in
- * case we abort. Note it is OK to do this after releasing the buffer,
- * because the heaptuples data structure is all in local memory, not in
- * the shared buffer.
- */
- if (IsCatalogRelation(relation))
- {
- for (i = 0; i < ntuples; i++)
- CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
- }
-
- /*
- * Copy t_self fields back to the caller's original tuples. This does
- * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
- * probably faster to always copy than check.
- */
- for (i = 0; i < ntuples; i++)
- tuples[i]->t_self = heaptuples[i]->t_self;
-
- pgstat_count_heap_insert(relation, ntuples);
-}
-
-/*
- * simple_heap_insert - insert a tuple
- *
- * Currently, this routine differs from heap_insert only in supplying
- * a default command ID and not allowing access to the speedup options.
- *
- * This should be used rather than using heap_insert directly in most places
- * where we are modifying system catalogs.
- */
-Oid
-simple_heap_insert(Relation relation, HeapTuple tup)
-{
- return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
-}
-
-/*
- * Given infomask/infomask2, compute the bits that must be saved in the
- * "infobits" field of xl_heap_delete, xl_heap_update, xl_heap_lock,
- * xl_heap_lock_updated WAL records.
- *
- * See fix_infomask_from_infobits.
- */
-static uint8
-compute_infobits(uint16 infomask, uint16 infomask2)
-{
- return
- ((infomask & HEAP_XMAX_IS_MULTI) != 0 ? XLHL_XMAX_IS_MULTI : 0) |
- ((infomask & HEAP_XMAX_LOCK_ONLY) != 0 ? XLHL_XMAX_LOCK_ONLY : 0) |
- ((infomask & HEAP_XMAX_EXCL_LOCK) != 0 ? XLHL_XMAX_EXCL_LOCK : 0) |
- /* note we ignore HEAP_XMAX_SHR_LOCK here */
- ((infomask & HEAP_XMAX_KEYSHR_LOCK) != 0 ? XLHL_XMAX_KEYSHR_LOCK : 0) |
- ((infomask2 & HEAP_KEYS_UPDATED) != 0 ?
- XLHL_KEYS_UPDATED : 0);
-}
-
-/*
- * Given two versions of the same t_infomask for a tuple, compare them and
- * return whether the relevant status for a tuple Xmax has changed. This is
- * used after a buffer lock has been released and reacquired: we want to ensure
- * that the tuple state continues to be the same it was when we previously
- * examined it.
- *
- * Note the Xmax field itself must be compared separately.
- */
-static inline bool
-xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
-{
- const uint16 interesting =
- HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
-
- if ((new_infomask & interesting) != (old_infomask & interesting))
- return true;
-
- return false;
-}
-
-/*
- * heap_delete - delete a tuple
- *
- * NB: do not call this directly unless you are prepared to deal with
- * concurrent-update conditions. Use simple_heap_delete instead.
- *
- * relation - table to be modified (caller must hold suitable lock)
- * tid - TID of tuple to be deleted
- * cid - delete command ID (used for visibility test, and stored into
- * cmax if successful)
- * crosscheck - if not InvalidSnapshot, also check tuple against this
- * wait - true if should wait for any conflicting update to commit/abort
- * hufd - output parameter, filled in failure cases (see below)
- *
- * Normal, successful return value is HeapTupleMayBeUpdated, which
- * actually means we did delete it. Failure return codes are
- * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
- * (the last only possible if wait == false).
- *
- * In the failure cases, the routine fills *hufd with the tuple's t_ctid,
- * t_xmax (resolving a possible MultiXact, if necessary), and t_cmax
- * (the last only for HeapTupleSelfUpdated, since we
- * cannot obtain cmax from a combocid generated by another transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
- */
-HTSU_Result
-heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd)
-{
- HTSU_Result result;
- TransactionId xid = GetCurrentTransactionId();
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
- Buffer vmbuffer = InvalidBuffer;
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
- bool have_tuple_lock = false;
- bool iscombo;
- bool all_visible_cleared = false;
- HeapTuple old_key_tuple = NULL; /* replica identity of the tuple */
- bool old_key_copied = false;
-
- Assert(ItemPointerIsValid(tid));
-
- /*
- * Forbid this during a parallel operation, lest it allocate a combocid.
- * Other workers might need that combocid for visibility checks, and we
- * have no provision for broadcasting it to them.
- */
- if (IsInParallelMode())
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
- errmsg("cannot delete tuples during a parallel operation")));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
/*
* Before locking the buffer, pin the visibility map page if it appears to
@@ -4523,7 +3850,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
/*
* Return the MultiXactStatus corresponding to the given tuple lock mode.
*/
-static MultiXactStatus
+MultiXactStatus
get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
{
int retval;
@@ -4541,1712 +3868,330 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
}
/*
- * heap_lock_tuple - lock a tuple in shared or exclusive mode
- *
- * Note that this acquires a buffer pin, which the caller must release.
- *
- * Input parameters:
- * relation: relation containing tuple (caller must hold suitable lock)
- * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
- * cid: current command ID (used for visibility test, and stored into
- * tuple's cmax if lock is successful)
- * mode: indicates if shared or exclusive tuple lock is desired
- * wait_policy: what to do if tuple lock is not available
- * follow_updates: if true, follow the update chain to also lock descendant
- * tuples.
- *
- * Output parameters:
- * *tuple: all fields filled in
- * *buffer: set to buffer holding tuple (pinned but not locked at exit)
- * *hufd: filled in failure cases (see below)
- *
- * Function result may be:
- * HeapTupleMayBeUpdated: lock was successfully acquired
- * HeapTupleInvisible: lock failed because tuple was never visible to us
- * HeapTupleSelfUpdated: lock failed because tuple updated by self
- * HeapTupleUpdated: lock failed because tuple updated by other xact
- * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ * Acquire heavyweight lock on the given tuple, in preparation for acquiring
+ * its normal, Xmax-based tuple lock.
*
- * In the failure cases other than HeapTupleInvisible, the routine fills
- * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
- * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
- * since we cannot obtain cmax from a combocid generated by another
- * transaction).
- * See comments for struct HeapUpdateFailureData for additional info.
+ * have_tuple_lock is an input and output parameter: on input, it indicates
+ * whether the lock has previously been acquired (and this function does
+ * nothing in that case). If this function returns success, have_tuple_lock
+ * has been flipped to true.
*
- * See README.tuplock for a thorough explanation of this mechanism.
+ * Returns false if it was unable to obtain the lock; this can only happen if
+ * wait_policy is Skip.
*/
-HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_updates,
- Buffer *buffer, HeapUpdateFailureData *hufd)
+bool
+heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
+ LockWaitPolicy wait_policy, bool *have_tuple_lock)
{
- HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- Page page;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
- TransactionId xid,
- xmax;
- uint16 old_infomask,
- new_infomask,
- new_infomask2;
- bool first_time = true;
- bool have_tuple_lock = false;
- bool cleared_all_frozen = false;
-
- *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
- block = ItemPointerGetBlockNumber(tid);
-
- /*
- * Before locking the buffer, pin the visibility map page if it appears to
- * be necessary. Since we haven't got the lock yet, someone else might be
- * in the middle of changing this, so we'll need to recheck after we have
- * the lock.
- */
- if (PageIsAllVisible(BufferGetPage(*buffer)))
- visibilitymap_pin(relation, block, &vmbuffer);
+ if (*have_tuple_lock)
+ return true;
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ LockTupleTuplock(relation, tid, mode);
+ break;
- page = BufferGetPage(*buffer);
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
+ case LockWaitSkip:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ return false;
+ break;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ case LockWaitError:
+ if (!ConditionalLockTupleTuplock(relation, tid, mode))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ *have_tuple_lock = true;
-l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
-
- if (result == HeapTupleInvisible)
- {
- /*
- * This is possible, but only when locking a tuple for ON CONFLICT
- * UPDATE. We return this value here rather than throwing an error in
- * order to give that case the opportunity to throw a more specific
- * error.
- */
- result = HeapTupleInvisible;
- goto out_locked;
- }
- else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
- {
- TransactionId xwait;
- uint16 infomask;
- uint16 infomask2;
- bool require_sleep;
- ItemPointerData t_ctid;
-
- /* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
-
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
- /*
- * If any subtransaction of the current top transaction already holds
- * a lock as strong as or stronger than what we're requesting, we
- * effectively hold the desired lock already. We *must* succeed
- * without trying to take the tuple lock, else we will deadlock
- * against anyone wanting to acquire a stronger lock.
- *
- * Note we only do this the first time we loop on the HTSU result;
- * there is no point in testing in subsequent passes, because
- * evidently our own transaction cannot have acquired a new lock after
- * the first time we checked.
- */
- if (first_time)
- {
- first_time = false;
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- int i;
- int nmembers;
- MultiXactMember *members;
-
- /*
- * We don't need to allow old multixacts here; if that had
- * been the case, HeapTupleSatisfiesUpdate would have returned
- * MayBeUpdated and we wouldn't be here.
- */
- nmembers =
- GetMultiXactIdMembers(xwait, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(infomask));
-
- for (i = 0; i < nmembers; i++)
- {
- /* only consider members of our own transaction */
- if (!TransactionIdIsCurrentTransactionId(members[i].xid))
- continue;
-
- if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
- {
- pfree(members);
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- }
-
- if (members)
- pfree(members);
- }
- else if (TransactionIdIsCurrentTransactionId(xwait))
- {
- switch (mode)
- {
- case LockTupleKeyShare:
- Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask));
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- case LockTupleShare:
- if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleNoKeyExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- case LockTupleExclusive:
- if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
- infomask2 & HEAP_KEYS_UPDATED)
- {
- result = HeapTupleMayBeUpdated;
- goto out_unlocked;
- }
- break;
- }
- }
- }
-
- /*
- * Initially assume that we will have to wait for the locking
- * transaction(s) to finish. We check various cases below in which
- * this can be turned off.
- */
- require_sleep = true;
- if (mode == LockTupleKeyShare)
- {
- /*
- * If we're requesting KeyShare, and there's no update present, we
- * don't need to wait. Even if there is an update, we can still
- * continue if the key hasn't been modified.
- *
- * However, if there are updates, we need to walk the update chain
- * to mark future versions of the row as locked, too. That way,
- * if somebody deletes that future version, we're protected
- * against the key going away. This locking of future versions
- * could block momentarily, if a concurrent transaction is
- * deleting a key; or it could return a value to the effect that
- * the transaction deleting the key has already committed. So we
- * do this before re-locking the buffer; otherwise this would be
- * prone to deadlocks.
- *
- * Note that the TID we're locking was grabbed before we unlocked
- * the buffer. For it to change while we're not looking, the
- * other properties we're testing for below after re-locking the
- * buffer would also change, in which case we would restart this
- * loop above.
- */
- if (!(infomask2 & HEAP_KEYS_UPDATED))
- {
- bool updated;
-
- updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
-
- /*
- * If there are updates, follow the update chain; bail out if
- * that cannot be done.
- */
- if (follow_updates && updated)
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * Also, if it wasn't updated before we released the lock, but
- * is updated now, we start over too; the reason is that we
- * now need to follow the update chain to lock the new
- * versions.
- */
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
- !updated))
- goto l3;
-
- /* Things look okay, so we can skip sleeping */
- require_sleep = false;
-
- /*
- * Note we allow Xmax to change here; other updaters/lockers
- * could have modified it before we grabbed the buffer lock.
- * However, this is not a problem, because with the recheck we
- * just did we ensure that they still don't conflict with the
- * lock we want.
- */
- }
- }
- else if (mode == LockTupleShare)
- {
- /*
- * If we're requesting Share, we can similarly avoid sleeping if
- * there's no update and no exclusive lock present.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
- !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Make sure it's still an appropriate lock, else start over.
- * See above about allowing xmax to change.
- */
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
- goto l3;
- require_sleep = false;
- }
- }
- else if (mode == LockTupleNoKeyExclusive)
- {
- /*
- * If we're requesting NoKeyExclusive, we might also be able to
- * avoid sleeping; just ensure that there no conflicting lock
- * already acquired.
- */
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
- mode))
- {
- /*
- * No conflict, but if the xmax changed under us in the
- * meantime, start over.
- */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
- else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- /* otherwise, we're good */
- require_sleep = false;
- }
- }
-
- /*
- * As a check independent from those above, we can also avoid sleeping
- * if the current transaction is the sole locker of the tuple. Note
- * that the strength of the lock already held is irrelevant; this is
- * not about recording the lock in Xmax (which will be done regardless
- * of this optimization, below). Also, note that the cases where we
- * hold a lock stronger than we are requesting are already handled
- * above by not doing anything.
- *
- * Note we only deal with the non-multixact case here; MultiXactIdWait
- * is well equipped to deal with this situation on its own.
- */
- if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
- TransactionIdIsCurrentTransactionId(xwait))
- {
- /* ... but if the xmax changed in the meantime, start over */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
- require_sleep = false;
- }
-
- /*
- * Time to sleep on the other transaction/multixact, if necessary.
- *
- * If the other transaction is an update that's already committed,
- * then sleeping cannot possibly do any good: if we're required to
- * sleep, get out to raise an error instead.
- *
- * By here, we either have already acquired the buffer exclusive lock,
- * or we must wait for the locking transaction or multixact; so below
- * we ensure that we grab buffer lock after the sleep.
- */
- if (require_sleep && result == HeapTupleUpdated)
- {
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- else if (require_sleep)
- {
- /*
- * Acquire tuple lock to establish our priority for the tuple, or
- * die trying. LockTuple will release us when we are next-in-line
- * for the tuple. We must do this even if we are share-locking.
- *
- * If we are forced to "start over" below, we keep the tuple lock;
- * this arranges that we stay at the head of the line while
- * rechecking tuple state.
- */
- if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
- &have_tuple_lock))
- {
- /*
- * This can only happen if wait_policy is Skip and the lock
- * couldn't be obtained.
- */
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
-
- if (infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus status = get_mxact_status_for_lock(mode, false);
-
- /* We only ever lock tuples, never update them */
- if (status >= MultiXactStatusNoKeyUpdate)
- elog(ERROR, "invalid lock mode in heap_lock_tuple");
-
- /* wait for multixact to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
- break;
- case LockWaitSkip:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
- status, infomask, relation,
- NULL))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
-
- break;
- }
-
- /*
- * Of course, the multixact might not be done here: if we're
- * requesting a light lock mode, other transactions with light
- * locks could still be alive, as well as locks owned by our
- * own xact or other subxacts of this backend. We need to
- * preserve the surviving MultiXact members. Note that it
- * isn't absolutely necessary in the latter case, but doing so
- * is simpler.
- */
- }
- else
- {
- /* wait for regular transaction to end, or die trying */
- switch (wait_policy)
- {
- case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
- XLTW_Lock);
- break;
- case LockWaitSkip:
- if (!ConditionalXactLockTableWait(xwait))
- {
- result = HeapTupleWouldBlock;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- break;
- case LockWaitError:
- if (!ConditionalXactLockTableWait(xwait))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- }
-
- /* if there are updates, follow the update chain */
- if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
- {
- HTSU_Result res;
-
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
- GetCurrentTransactionId(),
- mode);
- if (res != HeapTupleMayBeUpdated)
- {
- result = res;
- /* recovery code expects to have buffer lock held */
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto failed;
- }
- }
-
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * xwait is done, but if xwait had just locked the tuple then some
- * other xact could update this tuple before we get to this point.
- * Check for xmax change, and start over if so.
- */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
- xwait))
- goto l3;
-
- if (!(infomask & HEAP_XMAX_IS_MULTI))
- {
- /*
- * Otherwise check if it committed or aborted. Note we cannot
- * be here if the tuple was only locked by somebody who didn't
- * conflict with us; that would have been handled above. So
- * that transaction must necessarily be gone by now. But
- * don't check for this in the multixact case, because some
- * locker transactions might still be running.
- */
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
- }
- }
-
- /* By here, we're certain that we hold buffer exclusive lock again */
-
- /*
- * We may lock if previous xmax aborted, or if it committed but only
- * locked the tuple without updating it; or if we didn't have to wait
- * at all for whatever reason.
- */
- if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
- }
-
-failed:
- if (result != HeapTupleMayBeUpdated)
- {
- Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
- result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
- if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
- else
- hufd->cmax = InvalidCommandId;
- goto out_locked;
- }
-
- /*
- * If we didn't pin the visibility map page and the page has become all
- * visible while we were busy locking the buffer, or during some
- * subsequent window during which we had it unlocked, we'll have to unlock
- * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
- * unfortunate, especially since we'll now have to recheck whether the
- * tuple has been locked or updated under us, but hopefully it won't
- * happen very often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
- {
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(relation, block, &vmbuffer);
- LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- goto l3;
- }
-
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
-
- /*
- * If this is the first possibly-multixact-able operation in the current
- * transaction, set my per-backend OldestMemberMXactId setting. We can be
- * certain that the transaction will never become a member of any older
- * MultiXactIds than that. (We have to do this even if we end up just
- * using our own TransactionId below, since some other backend could
- * incorporate our XID into a MultiXact immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- /*
- * Compute the new xmax and infomask to store into the tuple. Note we do
- * not modify the tuple just yet, because that would leave it in the wrong
- * state if multixact.c elogs.
- */
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
- GetCurrentTransactionId(), mode, false,
- &xid, &new_infomask, &new_infomask2);
-
- START_CRIT_SECTION();
-
- /*
- * Store transaction information of xact locking the tuple.
- *
- * Note: Cmax is meaningless in this context, so don't set it; this avoids
- * possibly generating a useless combo CID. Moreover, if we're locking a
- * previously updated tuple, it's important to preserve the Cmax.
- *
- * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
- * we would break the HOT chain.
- */
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
-
- /*
- * Make sure there is no forward chain link in t_ctid. Note that in the
- * cases where the tuple has been updated, we must not overwrite t_ctid,
- * because it was set by the updater. Moreover, if the tuple has been
- * updated, we need to follow the update chain to lock the new versions of
- * the tuple as well.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
-
- /* Clear only the all-frozen bit on visibility map if needed */
- if (PageIsAllVisible(page) &&
- visibilitymap_clear(relation, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
-
- MarkBufferDirty(*buffer);
-
- /*
- * XLOG stuff. You might think that we don't need an XLOG record because
- * there is no state change worth restoring after a crash. You would be
- * wrong however: we have just written either a TransactionId or a
- * MultiXactId that may never have been seen on disk before, and we need
- * to make sure that there are XLOG entries covering those ID numbers.
- * Else the same IDs might be re-used after a crash, which would be
- * disastrous if this page made it to disk before the crash. Essentially
- * we have to enforce the WAL log-before-data rule even in this case.
- * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
- * entries for everything anyway.)
- */
- if (RelationNeedsWAL(relation))
- {
- xl_heap_lock xlrec;
- XLogRecPtr recptr;
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
- xlrec.locking_xid = xid;
- xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
- xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
- XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
-
- /* we don't decode row locks atm, so no need to log the origin */
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
-
-out_unlocked:
- if (BufferIsValid(vmbuffer))
- ReleaseBuffer(vmbuffer);
-
- /*
- * Don't update the visibility map here. Locking a tuple doesn't change
- * visibility info.
- */
-
- /*
- * Now that we have successfully marked the tuple as locked, we can
- * release the lmgr tuple lock, if we had it.
- */
- if (have_tuple_lock)
- UnlockTupleTuplock(relation, tid, mode);
-
- return result;
-}
-
-/*
- * Acquire heavyweight lock on the given tuple, in preparation for acquiring
- * its normal, Xmax-based tuple lock.
- *
- * have_tuple_lock is an input and output parameter: on input, it indicates
- * whether the lock has previously been acquired (and this function does
- * nothing in that case). If this function returns success, have_tuple_lock
- * has been flipped to true.
- *
- * Returns false if it was unable to obtain the lock; this can only happen if
- * wait_policy is Skip.
- */
-static bool
-heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
- LockWaitPolicy wait_policy, bool *have_tuple_lock)
-{
- if (*have_tuple_lock)
- return true;
-
- switch (wait_policy)
- {
- case LockWaitBlock:
- LockTupleTuplock(relation, tid, mode);
- break;
-
- case LockWaitSkip:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- return false;
- break;
-
- case LockWaitError:
- if (!ConditionalLockTupleTuplock(relation, tid, mode))
- ereport(ERROR,
- (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
- errmsg("could not obtain lock on row in relation \"%s\"",
- RelationGetRelationName(relation))));
- break;
- }
- *have_tuple_lock = true;
-
- return true;
-}
-
-/*
- * Given an original set of Xmax and infomask, and a transaction (identified by
- * add_to_xmax) acquiring a new lock of some mode, compute the new Xmax and
- * corresponding infomasks to use on the tuple.
- *
- * Note that this might have side effects such as creating a new MultiXactId.
- *
- * Most callers will have called HeapTupleSatisfiesUpdate before this function;
- * that will have set the HEAP_XMAX_INVALID bit if the xmax was a MultiXactId
- * but it was not running anymore. There is a race condition, which is that the
- * MultiXactId may have finished since then, but that uncommon case is handled
- * either here, or within MultiXactIdExpand.
- *
- * There is a similar race condition possible when the old xmax was a regular
- * TransactionId. We test TransactionIdIsInProgress again just to narrow the
- * window, but it's still possible to end up creating an unnecessary
- * MultiXactId. Fortunately this is harmless.
- */
-static void
-compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
- uint16 old_infomask2, TransactionId add_to_xmax,
- LockTupleMode mode, bool is_update,
- TransactionId *result_xmax, uint16 *result_infomask,
- uint16 *result_infomask2)
-{
- TransactionId new_xmax;
- uint16 new_infomask,
- new_infomask2;
-
- Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
-
-l5:
- new_infomask = 0;
- new_infomask2 = 0;
- if (old_infomask & HEAP_XMAX_INVALID)
- {
- /*
- * No previous locker; we just insert our own TransactionId.
- *
- * Note that it's critical that this case be the first one checked,
- * because there are several blocks below that come back to this one
- * to implement certain optimizations; old_infomask might contain
- * other dirty bits in those cases, but we don't really care.
- */
- if (is_update)
- {
- new_xmax = add_to_xmax;
- if (mode == LockTupleExclusive)
- new_infomask2 |= HEAP_KEYS_UPDATED;
- }
- else
- {
- new_infomask |= HEAP_XMAX_LOCK_ONLY;
- switch (mode)
- {
- case LockTupleKeyShare:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
- break;
- case LockTupleShare:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_SHR_LOCK;
- break;
- case LockTupleNoKeyExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- break;
- case LockTupleExclusive:
- new_xmax = add_to_xmax;
- new_infomask |= HEAP_XMAX_EXCL_LOCK;
- new_infomask2 |= HEAP_KEYS_UPDATED;
- break;
- default:
- new_xmax = InvalidTransactionId; /* silence compiler */
- elog(ERROR, "invalid lock mode");
- }
- }
- }
- else if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- MultiXactStatus new_status;
-
- /*
- * Currently we don't allow XMAX_COMMITTED to be set for multis, so
- * cross-check.
- */
- Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
-
- /*
- * A multixact together with LOCK_ONLY set but neither lock bit set
- * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
- * anymore. This check is critical for databases upgraded by
- * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
- * that such multis are never passed.
- */
- if (HEAP_LOCKED_UPGRADED(old_infomask))
- {
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /*
- * If the XMAX is already a MultiXactId, then we need to expand it to
- * include add_to_xmax; but if all the members were lockers and are
- * all gone, we can do away with the IS_MULTI bit and just set
- * add_to_xmax as the only locker/updater. If all lockers are gone
- * and we have an updater that aborted, we can also do without a
- * multi.
- *
- * The cost of doing GetMultiXactIdMembers would be paid by
- * MultiXactIdExpand if we weren't to do this, so this check is not
- * incurring extra work anyhow.
- */
- if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
- {
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
- !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
- old_infomask)))
- {
- /*
- * Reset these bits and restart; otherwise fall through to
- * create a new multi below.
- */
- old_infomask &= ~HEAP_XMAX_IS_MULTI;
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
- }
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
- new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (old_infomask & HEAP_XMAX_COMMITTED)
- {
- /*
- * It's a committed update, so we need to preserve him as updater of
- * the tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (TransactionIdIsInProgress(xmax))
- {
- /*
- * If the XMAX is a valid, in-progress TransactionId, then we need to
- * create a new MultiXactId that includes both the old locker or
- * updater and our own TransactionId.
- */
- MultiXactStatus new_status;
- MultiXactStatus old_status;
- LockTupleMode old_mode;
-
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- old_status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusForUpdate;
- else
- old_status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY can be present alone only when a page has been
- * upgraded by pg_upgrade. But in that case,
- * TransactionIdIsInProgress() should have returned false. We
- * assume it's no longer locked in this case.
- */
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
- old_infomask |= HEAP_XMAX_INVALID;
- old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
- goto l5;
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- old_status = MultiXactStatusUpdate;
- else
- old_status = MultiXactStatusNoKeyUpdate;
- }
-
- old_mode = TUPLOCK_from_mxstatus(old_status);
-
- /*
- * If the lock to be acquired is for the same TransactionId as the
- * existing lock, there's an optimization possible: consider only the
- * strongest of both locks as the only one present, and restart.
- */
- if (xmax == add_to_xmax)
- {
- /*
- * Note that it's not possible for the original tuple to be
- * updated: we wouldn't be here because the tuple would have been
- * invisible and we wouldn't try to update it. As a subtlety,
- * this code can also run when traversing an update chain to lock
- * future versions of a tuple. But we wouldn't be here either,
- * because the add_to_xmax would be different from the original
- * updater.
- */
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
-
- /* acquire the strongest of both */
- if (mode < old_mode)
- mode = old_mode;
- /* mustn't touch is_update */
-
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- /* otherwise, just fall back to creating a new multixact */
- new_status = get_mxact_status_for_lock(mode, is_update);
- new_xmax = MultiXactIdCreate(xmax, old_status,
- add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
- TransactionIdDidCommit(xmax))
- {
- /*
- * It's a committed update, so we gotta preserve him as updater of the
- * tuple.
- */
- MultiXactStatus status;
- MultiXactStatus new_status;
-
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
-
- new_status = get_mxact_status_for_lock(mode, is_update);
-
- /*
- * since it's not running, it's obviously impossible for the old
- * updater to be identical to the current one, so we need not check
- * for that case as we do in the block above.
- */
- new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
- GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
- }
- else
- {
- /*
- * Can get here iff the locking/updating transaction was running when
- * the infomask was extracted from the tuple, but finished before
- * TransactionIdIsInProgress got to run. Deal with it as if there was
- * no locker at all in the first place.
- */
- old_infomask |= HEAP_XMAX_INVALID;
- goto l5;
- }
-
- *result_infomask = new_infomask;
- *result_infomask2 = new_infomask2;
- *result_xmax = new_xmax;
-}
-
-/*
- * Subroutine for heap_lock_updated_tuple_rec.
- *
- * Given a hypothetical multixact status held by the transaction identified
- * with the given xid, does the current transaction need to wait, fail, or can
- * it continue if it wanted to acquire a lock of the given mode? "needwait"
- * is set to true if waiting is necessary; if it can continue, then
- * HeapTupleMayBeUpdated is returned. If the lock is already held by the
- * current transaction, return HeapTupleSelfUpdated. In case of a conflict
- * with another transaction, a different HeapTupleSatisfiesUpdate return code
- * is returned.
- *
- * The held status is said to be hypothetical because it might correspond to a
- * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
- * way for simplicity of API.
- */
-static HTSU_Result
-test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
- LockTupleMode mode, bool *needwait)
-{
- MultiXactStatus wantedstatus;
-
- *needwait = false;
- wantedstatus = get_mxact_status_for_lock(mode, false);
-
- /*
- * Note: we *must* check TransactionIdIsInProgress before
- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
- * explanation.
- */
- if (TransactionIdIsCurrentTransactionId(xid))
- {
- /*
- * The tuple has already been locked by our own transaction. This is
- * very rare but can happen if multiple transactions are trying to
- * lock an ancient version of the same tuple.
- */
- return HeapTupleSelfUpdated;
- }
- else if (TransactionIdIsInProgress(xid))
- {
- /*
- * If the locking transaction is running, what we do depends on
- * whether the lock modes conflict: if they do, then we must wait for
- * it to finish; otherwise we can fall through to lock this tuple
- * version without waiting.
- */
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- {
- *needwait = true;
- }
-
- /*
- * If we set needwait above, then this value doesn't matter;
- * otherwise, this value signals to caller that it's okay to proceed.
- */
- return HeapTupleMayBeUpdated;
- }
- else if (TransactionIdDidAbort(xid))
- return HeapTupleMayBeUpdated;
- else if (TransactionIdDidCommit(xid))
- {
- /*
- * The other transaction committed. If it was only a locker, then the
- * lock is completely gone now and we can return success; but if it
- * was an update, then what we do depends on whether the two lock
- * modes conflict. If they conflict, then we must report error to
- * caller. But if they don't, we can fall through to allow the current
- * transaction to lock the tuple.
- *
- * Note: the reason we worry about ISUPDATE here is because as soon as
- * a transaction ends, all its locks are gone and meaningless, and
- * thus we can ignore them; whereas its updates persist. In the
- * TransactionIdIsInProgress case, above, we don't need to check
- * because we know the lock is still "alive" and thus a conflict needs
- * always be checked.
- */
- if (!ISUPDATE_from_mxstatus(status))
- return HeapTupleMayBeUpdated;
-
- if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
- LOCKMODE_from_mxstatus(wantedstatus)))
- /* bummer */
- return HeapTupleUpdated;
-
- return HeapTupleMayBeUpdated;
- }
-
- /* Not in progress, not aborted, not committed -- must have crashed */
- return HeapTupleMayBeUpdated;
-}
-
-
-/*
- * Recursive part of heap_lock_updated_tuple
- *
- * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
- * xid with the given mode; if this tuple is updated, recurse to lock the new
- * version as well.
- */
-static HTSU_Result
-heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
- LockTupleMode mode)
-{
- HTSU_Result result;
- ItemPointerData tupid;
- HeapTupleData mytup;
- Buffer buf;
- uint16 new_infomask,
- new_infomask2,
- old_infomask,
- old_infomask2;
- TransactionId xmax,
- new_xmax;
- TransactionId priorXmax = InvalidTransactionId;
- bool cleared_all_frozen = false;
- Buffer vmbuffer = InvalidBuffer;
- BlockNumber block;
-
- ItemPointerCopy(tid, &tupid);
-
- for (;;)
- {
- new_infomask = 0;
- new_xmax = InvalidTransactionId;
- block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
-
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
- {
- /*
- * if we fail to find the updated version of the tuple, it's
- * because it was vacuumed/pruned away after its creator
- * transaction aborted. So behave as if we got to the end of the
- * chain, and there's no further tuple to lock: return success to
- * caller.
- */
- return HeapTupleMayBeUpdated;
- }
-
-l4:
- CHECK_FOR_INTERRUPTS();
-
- /*
- * Before locking the buffer, pin the visibility map page if it
- * appears to be necessary. Since we haven't got the lock yet,
- * someone else might be in the middle of changing this, so we'll need
- * to recheck after we have the lock.
- */
- if (PageIsAllVisible(BufferGetPage(buf)))
- visibilitymap_pin(rel, block, &vmbuffer);
- else
- vmbuffer = InvalidBuffer;
-
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * If we didn't pin the visibility map page and the page has become
- * all visible while we were busy locking the buffer, we'll have to
- * unlock and re-lock, to avoid holding the buffer lock across I/O.
- * That's a bit unfortunate, but hopefully shouldn't happen often.
- */
- if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- visibilitymap_pin(rel, block, &vmbuffer);
- LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
- }
-
- /*
- * Check the tuple XMIN against prior XMAX, if any. If we reached the
- * end of the chain, we're done, so return success.
- */
- if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
- priorXmax))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /*
- * Also check Xmin: if this tuple was created by an aborted
- * (sub)transaction, then we already locked the last live one in the
- * chain, thus we're done, so return success.
- */
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
- {
- UnlockReleaseBuffer(buf);
- return HeapTupleMayBeUpdated;
- }
-
- old_infomask = mytup.t_data->t_infomask;
- old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
-
- /*
- * If this tuple version has been updated or locked by some concurrent
- * transaction(s), what we do depends on whether our lock mode
- * conflicts with what those other transactions hold, and also on the
- * status of them.
- */
- if (!(old_infomask & HEAP_XMAX_INVALID))
- {
- TransactionId rawxmax;
- bool needwait;
-
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
- if (old_infomask & HEAP_XMAX_IS_MULTI)
- {
- int nmembers;
- int i;
- MultiXactMember *members;
-
- /*
- * We don't need a test for pg_upgrade'd tuples: this is only
- * applied to tuples after the first in an update chain. Said
- * first tuple in the chain may well be locked-in-9.2-and-
- * pg_upgraded, but that one was already locked by our caller,
- * not us; and any subsequent ones cannot be because our
- * caller must necessarily have obtained a snapshot later than
- * the pg_upgrade itself.
- */
- Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
-
- nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
- HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- for (i = 0; i < nmembers; i++)
- {
- result = test_lockmode_for_conflict(members[i].status,
- members[i].xid,
- mode, &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a
- * previous iteration of this (say heap_lock_tuple was
- * forced to restart the locking loop because of a change
- * in xmax), then we hold the lock already on this tuple
- * version and we don't need to do anything; and this is
- * not an error condition either. We just need to skip
- * this tuple and continue locking the next version in the
- * update chain.
- */
- if (result == HeapTupleSelfUpdated)
- {
- pfree(members);
- goto next;
- }
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(members[i].xid, rel,
- &mytup.t_self,
- XLTW_LockUpdated);
- pfree(members);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- pfree(members);
- goto out_locked;
- }
- }
- if (members)
- pfree(members);
- }
- else
- {
- MultiXactStatus status;
-
- /*
- * For a non-multi Xmax, we first need to compute the
- * corresponding MultiXactStatus by using the infomask bits.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
- {
- if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
- status = MultiXactStatusForKeyShare;
- else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
- status = MultiXactStatusForShare;
- else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
- {
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusForUpdate;
- else
- status = MultiXactStatusForNoKeyUpdate;
- }
- else
- {
- /*
- * LOCK_ONLY present alone (a pg_upgraded tuple marked
- * as share-locked in the old cluster) shouldn't be
- * seen in the middle of an update chain.
- */
- elog(ERROR, "invalid lock status in tuple");
- }
- }
- else
- {
- /* it's an update, but which kind? */
- if (old_infomask2 & HEAP_KEYS_UPDATED)
- status = MultiXactStatusUpdate;
- else
- status = MultiXactStatusNoKeyUpdate;
- }
-
- result = test_lockmode_for_conflict(status, rawxmax, mode,
- &needwait);
-
- /*
- * If the tuple was already locked by ourselves in a previous
- * iteration of this (say heap_lock_tuple was forced to
- * restart the locking loop because of a change in xmax), then
- * we hold the lock already on this tuple version and we don't
- * need to do anything; and this is not an error condition
- * either. We just need to skip this tuple and continue
- * locking the next version in the update chain.
- */
- if (result == HeapTupleSelfUpdated)
- goto next;
-
- if (needwait)
- {
- LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- XactLockTableWait(rawxmax, rel, &mytup.t_self,
- XLTW_LockUpdated);
- goto l4;
- }
- if (result != HeapTupleMayBeUpdated)
- {
- goto out_locked;
- }
- }
- }
-
- /* compute the new Xmax and infomask values for the tuple ... */
- compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
- xid, mode, false,
- &new_xmax, &new_infomask, &new_infomask2);
-
- if (PageIsAllVisible(BufferGetPage(buf)) &&
- visibilitymap_clear(rel, block, vmbuffer,
- VISIBILITYMAP_ALL_FROZEN))
- cleared_all_frozen = true;
-
- START_CRIT_SECTION();
-
- /* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
- mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
- mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- mytup.t_data->t_infomask |= new_infomask;
- mytup.t_data->t_infomask2 |= new_infomask2;
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(rel))
- {
- xl_heap_lock_updated xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
- xlrec.xmax = new_xmax;
- xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
- xlrec.flags =
- cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
-
- recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
-next:
- /* if we find the end of update chain, we're done. */
- if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
- ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
- {
- result = HeapTupleMayBeUpdated;
- goto out_locked;
- }
-
- /* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
- ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
- UnlockReleaseBuffer(buf);
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
- }
-
- result = HeapTupleMayBeUpdated;
-
-out_locked:
- UnlockReleaseBuffer(buf);
-
- if (vmbuffer != InvalidBuffer)
- ReleaseBuffer(vmbuffer);
-
- return result;
-
-}
+ return true;
+}
/*
- * heap_lock_updated_tuple
- * Follow update chain when locking an updated tuple, acquiring locks (row
- * marks) on the updated versions.
- *
- * The initial tuple is assumed to be already locked.
- *
- * This function doesn't check visibility, it just unconditionally marks the
- * tuple(s) as locked. If any tuple in the updated chain is being deleted
- * concurrently (or updated with the key being modified), sleep until the
- * transaction doing it is finished.
+ * Given an original set of Xmax and infomask, and a transaction (identified by
+ * add_to_xmax) acquiring a new lock of some mode, compute the new Xmax and
+ * corresponding infomasks to use on the tuple.
*
- * Note that we don't acquire heavyweight tuple locks on the tuples we walk
- * when we have to wait for other transactions to release them, as opposed to
- * what heap_lock_tuple does. The reason is that having more than one
- * transaction walking the chain is probably uncommon enough that risk of
- * starvation is not likely: one of the preconditions for being here is that
- * the snapshot in use predates the update that created this tuple (because we
- * started at an earlier version of the tuple), but at the same time such a
- * transaction cannot be using repeatable read or serializable isolation
- * levels, because that would lead to a serializability failure.
- */
-static HTSU_Result
-heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
- TransactionId xid, LockTupleMode mode)
-{
- if (!ItemPointerEquals(&tuple->t_self, ctid))
- {
- /*
- * If this is the first possibly-multixact-able operation in the
- * current transaction, set my per-backend OldestMemberMXactId
- * setting. We can be certain that the transaction will never become a
- * member of any older MultiXactIds than that. (We have to do this
- * even if we end up just using our own TransactionId below, since
- * some other backend could incorporate our XID into a MultiXact
- * immediately afterwards.)
- */
- MultiXactIdSetOldestMember();
-
- return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
- }
-
- /* nothing to lock */
- return HeapTupleMayBeUpdated;
-}
-
-/*
- * heap_finish_speculative - mark speculative insertion as successful
+ * Note that this might have side effects such as creating a new MultiXactId.
*
- * To successfully finish a speculative insertion we have to clear speculative
- * token from tuple. To do so the t_ctid field, which will contain a
- * speculative token value, is modified in place to point to the tuple itself,
- * which is characteristic of a newly inserted ordinary tuple.
+ * Most callers will have called HeapTupleSatisfiesUpdate before this function;
+ * that will have set the HEAP_XMAX_INVALID bit if the xmax was a MultiXactId
+ * but it was not running anymore. There is a race condition, which is that the
+ * MultiXactId may have finished since then, but that uncommon case is handled
+ * either here, or within MultiXactIdExpand.
*
- * NB: It is not ok to commit without either finishing or aborting a
- * speculative insertion. We could treat speculative tuples of committed
- * transactions implicitly as completed, but then we would have to be prepared
- * to deal with speculative tokens on committed tuples. That wouldn't be
- * difficult - no-one looks at the ctid field of a tuple with invalid xmax -
- * but clearing the token at completion isn't very expensive either.
- * An explicit confirmation WAL record also makes logical decoding simpler.
+ * There is a similar race condition possible when the old xmax was a regular
+ * TransactionId. We test TransactionIdIsInProgress again just to narrow the
+ * window, but it's still possible to end up creating an unnecessary
+ * MultiXactId. Fortunately this is harmless.
*/
void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
+compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2)
{
- Buffer buffer;
- Page page;
- OffsetNumber offnum;
- ItemId lp = NULL;
- HeapTupleHeader htup;
-
- buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- page = (Page) BufferGetPage(buffer);
-
- offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
- if (PageGetMaxOffsetNumber(page) >= offnum)
- lp = PageGetItemId(page, offnum);
-
- if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
- elog(ERROR, "invalid lp");
-
- htup = (HeapTupleHeader) PageGetItem(page, lp);
-
- /* SpecTokenOffsetNumber should be distinguishable from any real offset */
- StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
- "invalid speculative token constant");
-
- /* NO EREPORT(ERROR) from here till changes are logged */
- START_CRIT_SECTION();
-
- Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
-
- MarkBufferDirty(buffer);
+ TransactionId new_xmax;
+ uint16 new_infomask,
+ new_infomask2;
- /*
- * Replace the speculative insertion token with a real t_ctid, pointing to
- * itself like it does on regular tuples.
- */
- htup->t_ctid = tuple->t_self;
+ Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
- /* XLOG stuff */
- if (RelationNeedsWAL(relation))
+l5:
+ new_infomask = 0;
+ new_infomask2 = 0;
+ if (old_infomask & HEAP_XMAX_INVALID)
{
- xl_heap_confirm xlrec;
- XLogRecPtr recptr;
-
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
-
- XLogBeginInsert();
-
- /* We want the same filtering on this as on a plain insert */
- XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
-
- XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
-
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
-
- PageSetLSN(page, recptr);
- }
-
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buffer);
-}
-
-/*
- * heap_abort_speculative - kill a speculatively inserted tuple
- *
- * Marks a tuple that was speculatively inserted in the same command as dead,
- * by setting its xmin as invalid. That makes it immediately appear as dead
- * to all transactions, including our own. In particular, it makes
- * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
- * inserting a duplicate key value won't unnecessarily wait for our whole
- * transaction to finish (it'll just wait for our speculative insertion to
- * finish).
- *
- * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
- * that arise due to a mutual dependency that is not user visible. By
- * definition, unprincipled deadlocks cannot be prevented by the user
- * reordering lock acquisition in client code, because the implementation level
- * lock acquisitions are not under the user's direct control. If speculative
- * inserters did not take this precaution, then under high concurrency they
- * could deadlock with each other, which would not be acceptable.
- *
- * This is somewhat redundant with heap_delete, but we prefer to have a
- * dedicated routine with stripped down requirements. Note that this is also
- * used to delete the TOAST tuples created during speculative insertion.
- *
- * This routine does not affect logical decoding as it only looks at
- * confirmation records.
- */
-void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
-{
- TransactionId xid = GetCurrentTransactionId();
- ItemPointer tid = &(tuple->t_self);
- ItemId lp;
- HeapTupleData tp;
- Page page;
- BlockNumber block;
- Buffer buffer;
-
- Assert(ItemPointerIsValid(tid));
-
- block = ItemPointerGetBlockNumber(tid);
- buffer = ReadBuffer(relation, block);
- page = BufferGetPage(buffer);
-
- LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
- /*
- * Page can't be all visible, we just inserted into it, and are still
- * running.
- */
- Assert(!PageIsAllVisible(page));
-
- lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
- Assert(ItemIdIsNormal(lp));
-
- tp.t_tableOid = RelationGetRelid(relation);
- tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tp.t_len = ItemIdGetLength(lp);
- tp.t_self = *tid;
-
- /*
- * Sanity check that the tuple really is a speculatively inserted tuple,
- * inserted by us.
- */
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
- elog(ERROR, "attempted to kill a tuple inserted by another transaction");
- if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
- elog(ERROR, "attempted to kill a non-speculative tuple");
- Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+ /*
+ * No previous locker; we just insert our own TransactionId.
+ *
+ * Note that it's critical that this case be the first one checked,
+ * because there are several blocks below that come back to this one
+ * to implement certain optimizations; old_infomask might contain
+ * other dirty bits in those cases, but we don't really care.
+ */
+ if (is_update)
+ {
+ new_xmax = add_to_xmax;
+ if (mode == LockTupleExclusive)
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ }
+ else
+ {
+ new_infomask |= HEAP_XMAX_LOCK_ONLY;
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+ break;
+ case LockTupleShare:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_SHR_LOCK;
+ break;
+ case LockTupleNoKeyExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ break;
+ case LockTupleExclusive:
+ new_xmax = add_to_xmax;
+ new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ new_infomask2 |= HEAP_KEYS_UPDATED;
+ break;
+ default:
+ new_xmax = InvalidTransactionId; /* silence compiler */
+ elog(ERROR, "invalid lock mode");
+ }
+ }
+ }
+ else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus new_status;
- /*
- * No need to check for serializable conflicts here. There is never a
- * need for a combocid, either. No need to extract replica identity, or
- * do anything special with infomask bits.
- */
+ /*
+ * Currently we don't allow XMAX_COMMITTED to be set for multis, so
+ * cross-check.
+ */
+ Assert(!(old_infomask & HEAP_XMAX_COMMITTED));
- START_CRIT_SECTION();
+ /*
+ * A multixact together with LOCK_ONLY set but neither lock bit set
+ * (i.e. a pg_upgraded share locked tuple) cannot possibly be running
+ * anymore. This check is critical for databases upgraded by
+ * pg_upgrade; both MultiXactIdIsRunning and MultiXactIdExpand assume
+ * that such multis are never passed.
+ */
+ if (HEAP_LOCKED_UPGRADED(old_infomask))
+ {
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- /*
- * The tuple will become DEAD immediately. Flag that this page
- * immediately is a candidate for pruning by setting xmin to
- * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
- * inventing a nicer API for this.
- */
- Assert(TransactionIdIsValid(RecentGlobalXmin));
- PageSetPrunable(page, RecentGlobalXmin);
+ /*
+ * If the XMAX is already a MultiXactId, then we need to expand it to
+ * include add_to_xmax; but if all the members were lockers and are
+ * all gone, we can do away with the IS_MULTI bit and just set
+ * add_to_xmax as the only locker/updater. If all lockers are gone
+ * and we have an updater that aborted, we can also do without a
+ * multi.
+ *
+ * The cost of doing GetMultiXactIdMembers would be paid by
+ * MultiXactIdExpand if we weren't to do this, so this check is not
+ * incurring extra work anyhow.
+ */
+ if (!MultiXactIdIsRunning(xmax, HEAP_XMAX_IS_LOCKED_ONLY(old_infomask)))
+ {
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) ||
+ !TransactionIdDidCommit(MultiXactIdGetUpdateXid(xmax,
+ old_infomask)))
+ {
+ /*
+ * Reset these bits and restart; otherwise fall through to
+ * create a new multi below.
+ */
+ old_infomask &= ~HEAP_XMAX_IS_MULTI;
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
+ }
- /* store transaction information of xact deleting the tuple */
- tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
- tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * Set the tuple header xmin to InvalidTransactionId. This makes the
- * tuple immediately invisible everyone. (In particular, to any
- * transactions waiting on the speculative token, woken up later.)
- */
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax,
+ new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (old_infomask & HEAP_XMAX_COMMITTED)
+ {
+ /*
+ * It's a committed update, so we need to preserve him as updater of
+ * the tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- /* Clear the speculative insertion token too */
- tp.t_data->t_ctid = tp.t_self;
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- MarkBufferDirty(buffer);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- /*
- * XLOG stuff
- *
- * The WAL records generated here match heap_delete(). The same recovery
- * routines are used.
- */
- if (RelationNeedsWAL(relation))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else if (TransactionIdIsInProgress(xmax))
{
- xl_heap_delete xlrec;
- XLogRecPtr recptr;
+ /*
+ * If the XMAX is a valid, in-progress TransactionId, then we need to
+ * create a new MultiXactId that includes both the old locker or
+ * updater and our own TransactionId.
+ */
+ MultiXactStatus new_status;
+ MultiXactStatus old_status;
+ LockTupleMode old_mode;
- xlrec.flags = XLH_DELETE_IS_SUPER;
- xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
- tp.t_data->t_infomask2);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
- xlrec.xmax = xid;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ old_status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusForUpdate;
+ else
+ old_status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY can be present alone only when a page has been
+ * upgraded by pg_upgrade. But in that case,
+ * TransactionIdIsInProgress() should have returned false. We
+ * assume it's no longer locked in this case.
+ */
+ elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ old_infomask |= HEAP_XMAX_INVALID;
+ old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
+ goto l5;
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ old_status = MultiXactStatusUpdate;
+ else
+ old_status = MultiXactStatusNoKeyUpdate;
+ }
- XLogBeginInsert();
- XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
- XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+ old_mode = TUPLOCK_from_mxstatus(old_status);
+
+ /*
+ * If the lock to be acquired is for the same TransactionId as the
+ * existing lock, there's an optimization possible: consider only the
+ * strongest of both locks as the only one present, and restart.
+ */
+ if (xmax == add_to_xmax)
+ {
+ /*
+ * Note that it's not possible for the original tuple to be
+ * updated: we wouldn't be here because the tuple would have been
+ * invisible and we wouldn't try to update it. As a subtlety,
+ * this code can also run when traversing an update chain to lock
+ * future versions of a tuple. But we wouldn't be here either,
+ * because the add_to_xmax would be different from the original
+ * updater.
+ */
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
- /* No replica identity & replication origin logged */
+ /* acquire the strongest of both */
+ if (mode < old_mode)
+ mode = old_mode;
+ /* mustn't touch is_update */
- recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
+ }
- PageSetLSN(page, recptr);
+ /* otherwise, just fall back to creating a new multixact */
+ new_status = get_mxact_status_for_lock(mode, is_update);
+ new_xmax = MultiXactIdCreate(xmax, old_status,
+ add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
}
+ else if (!HEAP_XMAX_IS_LOCKED_ONLY(old_infomask) &&
+ TransactionIdDidCommit(xmax))
+ {
+ /*
+ * It's a committed update, so we gotta preserve him as updater of the
+ * tuple.
+ */
+ MultiXactStatus status;
+ MultiXactStatus new_status;
- END_CRIT_SECTION();
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ new_status = get_mxact_status_for_lock(mode, is_update);
- if (HeapTupleHasExternal(&tp))
+ /*
+ * since it's not running, it's obviously impossible for the old
+ * updater to be identical to the current one, so we need not check
+ * for that case as we do in the block above.
+ */
+ new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_status);
+ GetMultiXactIdHintBits(new_xmax, &new_infomask, &new_infomask2);
+ }
+ else
{
- Assert(!IsToastRelation(relation));
- toast_delete(relation, &tp, true);
+ /*
+ * Can get here iff the locking/updating transaction was running when
+ * the infomask was extracted from the tuple, but finished before
+ * TransactionIdIsInProgress got to run. Deal with it as if there was
+ * no locker at all in the first place.
+ */
+ old_infomask |= HEAP_XMAX_INVALID;
+ goto l5;
}
- /*
- * Never need to mark tuple for invalidation, since catalogs don't support
- * speculative insertion
- */
-
- /* Now we can release the buffer */
- ReleaseBuffer(buffer);
-
- /* count deletion, as we counted the insertion too */
- pgstat_count_heap_delete(relation);
+ *result_infomask = new_infomask;
+ *result_infomask2 = new_infomask2;
+ *result_xmax = new_xmax;
}
/*
@@ -7012,7 +4957,7 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
*
* The passed infomask pairs up with the given multixact in the tuple header.
*/
-static bool
+bool
DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
LockTupleMode lockmode)
{
@@ -7179,7 +5124,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static void
+void
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining)
@@ -7201,7 +5146,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
* We return (in *remaining, if not NULL) the number of members that are still
* running, including any (non-aborted) subtransactions of our own transaction.
*/
-static bool
+bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining)
{
@@ -7763,7 +5708,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
* tuples.
*/
-static XLogRecPtr
+XLogRecPtr
log_heap_new_cid(Relation relation, HeapTuple tup)
{
xl_heap_new_cid xlrec;
@@ -9139,46 +7084,6 @@ heap2_redo(XLogReaderState *record)
}
}
-/*
- * heap_sync - sync a heap, for use when no WAL has been written
- *
- * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and WAL is otherwise needed, we must force the
- * relation down to disk before it's safe to commit the transaction. This
- * requires writing out any dirty buffers and then doing a forced fsync.
- *
- * Indexes are not touched. (Currently, index operations associated with
- * the commands that use this are WAL-logged and so do not need fsync.
- * That behavior might change someday, but in any case it's likely that
- * any fsync decisions required would be per-index and hence not appropriate
- * to be done here.)
- */
-void
-heap_sync(Relation rel)
-{
- /* non-WAL-logged tables never need fsync */
- if (!RelationNeedsWAL(rel))
- return;
-
- /* main heap */
- FlushRelationBuffers(rel);
- /* FlushRelationBuffers will have opened rd_smgr */
- smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
-
- /* FSM is not critical, don't bother syncing it */
-
- /* toast heap, if any */
- if (OidIsValid(rel->rd_rel->reltoastrelid))
- {
- Relation toastrel;
-
- toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
- FlushRelationBuffers(toastrel);
- smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
- heap_close(toastrel, AccessShareLock);
- }
-}
-
/*
* Mask a heap page before performing consistency checks on it.
*/
diff --git a/src/backend/access/heap/heapam_common.c b/src/backend/access/heap/heapam_common.c
index 6ebe0b290c..342c575b8c 100644
--- a/src/backend/access/heap/heapam_common.c
+++ b/src/backend/access/heap/heapam_common.c
@@ -24,9 +24,11 @@
#include "access/heapam.h"
#include "access/heapam_common.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
+#include "executor/tuptable.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index a874e243c9..567e80de8c 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -48,6 +48,18 @@
#include "utils/rel.h"
#include "utils/tqual.h"
+/* local functions */
+static bool heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+static void heapam_finish_speculative(Relation relation, TupleTableSlot *slot);
+static void heapam_abort_speculative(Relation relation, TupleTableSlot *slot);
+
/*-------------------------------------------------------------------------
*
* POSTGRES "time qualification" code, ie, tuple visibility rules.
@@ -1688,10 +1700,2195 @@ HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
}
}
-Datum
-heapam_storage_handler(PG_FUNCTION_ARGS)
+
+/*
+ * heap_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+static bool
+heap_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ HeapTuple tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
{
- StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ ItemId lp;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ bool valid;
+
+ /*
+ * Fetch and pin the appropriate page of the relation.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+
+ /*
+ * Need share lock on buffer to examine tuple commit status.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * We'd better check for out-of-range offnum in case of VACUUM since the
+ * TID was obtained.
+ */
+ offnum = ItemPointerGetOffsetNumber(tid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * get the item line pointer corresponding to the requested tid
+ */
+ lp = PageGetItemId(page, offnum);
+
+ /*
+ * Must check for deleted tuple.
+ */
+ if (!ItemIdIsNormal(lp))
+ {
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+ return false;
+ }
+
+ /*
+ * fill in tuple fields and place it in stuple
+ */
+ ItemPointerCopy(tid, &(tuple->t_self));
+ tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple->t_len = ItemIdGetLength(lp);
+ tuple->t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * check time qualification of tuple, then release lock
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
+
+ if (valid)
+ PredicateLockTuple(relation, tuple, snapshot);
+
+ CheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (valid)
+ {
+ /*
+ * All checks passed, so return the tuple as valid. Caller is now
+ * responsible for releasing the buffer.
+ */
+ *userbuf = buffer;
+
+ /* Count the successful fetch against appropriate rel, if any */
+ if (stats_relation != NULL)
+ pgstat_count_heap_fetch(stats_relation);
+
+ return true;
+ }
+
+ /* Tuple failed time qual, but maybe caller wants to see it anyway. */
+ if (keep_buf)
+ *userbuf = buffer;
+ else
+ {
+ ReleaseBuffer(buffer);
+ *userbuf = InvalidBuffer;
+ }
+
+ return false;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is
+ * completely bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ if ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0))
+ {
+ Assert(IndexFunc != NULL);
+
+ if (options & HEAP_INSERT_SPECULATIVE)
+ {
+ bool specConflict = false;
+
+ *recheckIndexes = (IndexFunc) (slot, estate, true,
+ &specConflict,
+ arbiterIndexes);
+
+ /* adjust the tuple's state accordingly */
+ if (!specConflict)
+ heapam_finish_speculative(relation, slot);
+ else
+ {
+ heapam_abort_speculative(relation, slot);
+ slot->tts_specConflict = true;
+ }
+ }
+ else
+ {
+ *recheckIndexes = (IndexFunc) (slot, estate, false,
+ NULL, arbiterIndexes);
+ }
+ }
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck,
+ bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ /*
+ * Note: instead of having to update the old index tuples associated with
+ * the heap tuple, all we do is form and insert new index tuples. This is
+ * because UPDATEs are actually DELETEs and INSERTs, and index tuple
+ * deletion is done later by VACUUM (see notes in ExecDelete). All we do
+ * here is insert new index tuples. -cim 9/27/89
+ */
+
+ /*
+ * insert index entries for tuple
+ *
+ * Note: heap_update returns the tid (location) of the new tuple in the
+ * t_self field.
+ *
+ * If it's a HOT update, we mustn't insert new index entries.
+ */
+ if ((result == HeapTupleMayBeUpdated) &&
+ (estate->es_result_relation_info->ri_NumIndices > 0) &&
+ (!HeapTupleIsHeapOnly(tuple)))
+ *recheckIndexes = (IndexFunc) (slot, estate, false, NULL, NIL);
+
+ return result;
+}
+
+static void
+heapam_finish_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp = NULL;
+ HeapTupleHeader htup;
+
+ Assert(slot->tts_speculativeToken != 0);
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ page = (Page) BufferGetPage(buffer);
+
+ offnum = ItemPointerGetOffsetNumber(&(tuple->t_self));
+ if (PageGetMaxOffsetNumber(page) >= offnum)
+ lp = PageGetItemId(page, offnum);
+
+ if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
+ elog(ERROR, "invalid lp");
+
+ htup = (HeapTupleHeader) PageGetItem(page, lp);
+
+ /* SpecTokenOffsetNumber should be distinguishable from any real offset */
+ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber,
+ "invalid speculative token constant");
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ Assert(HeapTupleHeaderIsSpeculative(tuple->t_data));
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * Replace the speculative insertion token with a real t_ctid, pointing to
+ * itself like it does on regular tuples.
+ */
+ htup->t_ctid = tuple->t_self;
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_confirm xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+
+ XLogBeginInsert();
+
+ /* We want the same filtering on this as on a plain insert */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapConfirm);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_CONFIRM);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
+}
+
+static void
+heapam_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
+ TransactionId xid = GetCurrentTransactionId();
+ ItemPointer tid = &(tuple->t_self);
+ ItemId lp;
+ HeapTupleData tp;
+ Page page;
+ BlockNumber block;
+ Buffer buffer;
+
+ /*
+ * Assert(slot->tts_speculativeToken != 0); This needs some update in
+ * toast
+ */
+ Assert(ItemPointerIsValid(tid));
+
+ block = ItemPointerGetBlockNumber(tid);
+ buffer = ReadBuffer(relation, block);
+ page = BufferGetPage(buffer);
+
+ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Page can't be all visible, we just inserted into it, and are still
+ * running.
+ */
+ Assert(!PageIsAllVisible(page));
+
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tp.t_tableOid = RelationGetRelid(relation);
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_self = *tid;
+
+ /*
+ * Sanity check that the tuple really is a speculatively inserted tuple,
+ * inserted by us.
+ */
+ if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ elog(ERROR, "attempted to kill a tuple inserted by another transaction");
+ if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
+ elog(ERROR, "attempted to kill a non-speculative tuple");
+ Assert(!HeapTupleHeaderIsHeapOnly(tp.t_data));
+
+ /*
+ * No need to check for serializable conflicts here. There is never a
+ * need for a combocid, either. No need to extract replica identity, or
+ * do anything special with infomask bits.
+ */
+
+ START_CRIT_SECTION();
+
+ /*
+ * The tuple will become DEAD immediately. Flag that this page
+ * immediately is a candidate for pruning by setting xmin to
+ * RecentGlobalXmin. That's not pretty, but it doesn't seem worth
+ * inventing a nicer API for this.
+ */
+ Assert(TransactionIdIsValid(RecentGlobalXmin));
+ PageSetPrunable(page, RecentGlobalXmin);
+
+ /* store transaction information of xact deleting the tuple */
+ tp.t_data->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
+ tp.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+
+ /*
+ * Set the tuple header xmin to InvalidTransactionId. This makes the
+ * tuple immediately invisible everyone. (In particular, to any
+ * transactions waiting on the speculative token, woken up later.)
+ */
+ HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+
+ /* Clear the speculative insertion token too */
+ tp.t_data->t_ctid = tp.t_self;
+
+ MarkBufferDirty(buffer);
+
+ /*
+ * XLOG stuff
+ *
+ * The WAL records generated here match heap_delete(). The same recovery
+ * routines are used.
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_delete xlrec;
+ XLogRecPtr recptr;
+
+ xlrec.flags = XLH_DELETE_IS_SUPER;
+ xlrec.infobits_set = compute_infobits(tp.t_data->t_infomask,
+ tp.t_data->t_infomask2);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tp.t_self);
+ xlrec.xmax = xid;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapDelete);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ /* No replica identity & replication origin logged */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ if (HeapTupleHasExternal(&tp))
+ {
+ Assert(!IsToastRelation(relation));
+ toast_delete(relation, &tp, true);
+ }
+
+ /*
+ * Never need to mark tuple for invalidation, since catalogs don't support
+ * speculative insertion
+ */
+
+ /* Now we can release the buffer */
+ ReleaseBuffer(buffer);
+
+ /* count deletion, as we counted the insertion too */
+ pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
+}
+
+/*
+ * heapam_multi_insert - insert multiple tuple into a heap
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+static void
+heapam_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ TransactionId xid = GetCurrentTransactionId();
+ HeapTuple *heaptuples;
+ int i;
+ int ndone;
+ char *scratch = NULL;
+ Page page;
+ bool needwal;
+ Size saveFreeSpace;
+ bool need_tuple_data = RelationIsLogicallyLogged(relation);
+ bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
+
+ needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
+ saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
+
+ /* Toast and set header data in all the tuples */
+ heaptuples = palloc(ntuples * sizeof(HeapTuple));
+ for (i = 0; i < ntuples; i++)
+ heaptuples[i] = heap_prepare_insert(relation, tuples[i],
+ xid, cid, options);
+
+ /*
+ * Allocate some memory to use for constructing the WAL record. Using
+ * palloc() within a critical section is not safe, so we allocate this
+ * beforehand.
+ */
+ if (needwal)
+ scratch = palloc(BLCKSZ);
+
+ /*
+ * We're about to do the actual inserts -- but check for conflict first,
+ * to minimize the possibility of having to roll back work we've just
+ * done.
+ *
+ * A check here does not definitively prevent a serialization anomaly;
+ * that check MUST be done at least past the point of acquiring an
+ * exclusive buffer content lock on every buffer that will be affected,
+ * and MAY be done after all inserts are reflected in the buffers and
+ * those locks are released; otherwise there race condition. Since
+ * multiple buffers can be locked and unlocked in the loop below, and it
+ * would not be feasible to identify and lock all of those buffers before
+ * the loop, we must do a final check at the end.
+ *
+ * The check here could be omitted with no loss of correctness; it is
+ * present strictly as an optimization.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call, which makes for a faster check.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ ndone = 0;
+ while (ndone < ntuples)
+ {
+ Buffer buffer;
+ Buffer vmbuffer = InvalidBuffer;
+ bool all_visible_cleared = false;
+ int nthispage;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Find buffer where at least the next tuple will fit. If the page is
+ * all-visible, this will also pin the requisite visibility map page.
+ */
+ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len,
+ InvalidBuffer, options, bistate,
+ &vmbuffer, NULL);
+ page = BufferGetPage(buffer);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
+ /*
+ * RelationGetBufferForTuple has ensured that the first tuple fits.
+ * Put that on the page, and then as many other tuples as fit.
+ */
+ RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
+ for (nthispage = 1; ndone + nthispage < ntuples; nthispage++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + nthispage];
+
+ if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
+ break;
+
+ RelationPutHeapTuple(relation, buffer, heaptup, false);
+
+ /*
+ * We don't use heap_multi_insert for catalog tuples yet, but
+ * better be prepared...
+ */
+ if (needwal && need_cids)
+ log_heap_new_cid(relation, heaptup);
+ }
+
+ if (PageIsAllVisible(page))
+ {
+ all_visible_cleared = true;
+ PageClearAllVisible(page);
+ visibilitymap_clear(relation,
+ BufferGetBlockNumber(buffer),
+ vmbuffer, VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * XXX Should we set PageSetPrunable on this page ? See heap_insert()
+ */
+
+ MarkBufferDirty(buffer);
+
+ /* XLOG stuff */
+ if (needwal)
+ {
+ XLogRecPtr recptr;
+ xl_heap_multi_insert *xlrec;
+ uint8 info = XLOG_HEAP2_MULTI_INSERT;
+ char *tupledata;
+ int totaldatalen;
+ char *scratchptr = scratch;
+ bool init;
+ int bufflags = 0;
+
+ /*
+ * If the page was previously empty, we can reinit the page
+ * instead of restoring the whole thing.
+ */
+ init = (ItemPointerGetOffsetNumber(&(heaptuples[ndone]->t_self)) == FirstOffsetNumber &&
+ PageGetMaxOffsetNumber(page) == FirstOffsetNumber + nthispage - 1);
+
+ /* allocate xl_heap_multi_insert struct from the scratch area */
+ xlrec = (xl_heap_multi_insert *) scratchptr;
+ scratchptr += SizeOfHeapMultiInsert;
+
+ /*
+ * Allocate offsets array. Unless we're reinitializing the page,
+ * in that case the tuples are stored in order starting at
+ * FirstOffsetNumber and we don't need to store the offsets
+ * explicitly.
+ */
+ if (!init)
+ scratchptr += nthispage * sizeof(OffsetNumber);
+
+ /* the rest of the scratch space is used for tuple data */
+ tupledata = scratchptr;
+
+ xlrec->flags = all_visible_cleared ? XLH_INSERT_ALL_VISIBLE_CLEARED : 0;
+ xlrec->ntuples = nthispage;
+
+ /*
+ * Write out an xl_multi_insert_tuple and the tuple data itself
+ * for each tuple.
+ */
+ for (i = 0; i < nthispage; i++)
+ {
+ HeapTuple heaptup = heaptuples[ndone + i];
+ xl_multi_insert_tuple *tuphdr;
+ int datalen;
+
+ if (!init)
+ xlrec->offsets[i] = ItemPointerGetOffsetNumber(&heaptup->t_self);
+ /* xl_multi_insert_tuple needs two-byte alignment. */
+ tuphdr = (xl_multi_insert_tuple *) SHORTALIGN(scratchptr);
+ scratchptr = ((char *) tuphdr) + SizeOfMultiInsertTuple;
+
+ tuphdr->t_infomask2 = heaptup->t_data->t_infomask2;
+ tuphdr->t_infomask = heaptup->t_data->t_infomask;
+ tuphdr->t_hoff = heaptup->t_data->t_hoff;
+
+ /* write bitmap [+ padding] [+ oid] + data */
+ datalen = heaptup->t_len - SizeofHeapTupleHeader;
+ memcpy(scratchptr,
+ (char *) heaptup->t_data + SizeofHeapTupleHeader,
+ datalen);
+ tuphdr->datalen = datalen;
+ scratchptr += datalen;
+ }
+ totaldatalen = scratchptr - tupledata;
+ Assert((scratchptr - scratch) < BLCKSZ);
+
+ if (need_tuple_data)
+ xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
+
+ /*
+ * Signal that this is the last xl_heap_multi_insert record
+ * emitted by this call to heap_multi_insert(). Needed for logical
+ * decoding so it knows when to cleanup temporary data.
+ */
+ if (ndone + nthispage == ntuples)
+ xlrec->flags |= XLH_INSERT_LAST_IN_MULTI;
+
+ if (init)
+ {
+ info |= XLOG_HEAP_INIT_PAGE;
+ bufflags |= REGBUF_WILL_INIT;
+ }
+
+ /*
+ * If we're doing logical decoding, include the new tuple data
+ * even if we take a full-page image of the page.
+ */
+ if (need_tuple_data)
+ bufflags |= REGBUF_KEEP_DATA;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) xlrec, tupledata - scratch);
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
+
+ XLogRegisterBufData(0, tupledata, totaldatalen);
+
+ /* filtering by origin on a row level is much more efficient */
+ XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+ recptr = XLogInsert(RM_HEAP2_ID, info);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ UnlockReleaseBuffer(buffer);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ ndone += nthispage;
+ }
+
+ /*
+ * We're done with the actual inserts. Check for conflicts again, to
+ * ensure that all rw-conflicts in to these inserts are detected. Without
+ * this final check, a sequential scan of the heap may have locked the
+ * table after the "before" check, missing one opportunity to detect the
+ * conflict, and then scanned the table before the new tuples were there,
+ * missing the other chance to detect the conflict.
+ *
+ * For heap inserts, we only need to check for table-level SSI locks. Our
+ * new tuples can't possibly conflict with existing tuple locks, and heap
+ * page locks are only consolidated versions of tuple locks; they do not
+ * lock "gaps" as index page locks do. So we don't need to specify a
+ * buffer when making the call.
+ */
+ CheckForSerializableConflictIn(relation, NULL, InvalidBuffer);
+
+ /*
+ * If tuples are cachable, mark them for invalidation from the caches in
+ * case we abort. Note it is OK to do this after releasing the buffer,
+ * because the heaptuples data structure is all in local memory, not in
+ * the shared buffer.
+ */
+ if (IsCatalogRelation(relation))
+ {
+ for (i = 0; i < ntuples; i++)
+ CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
+ }
+
+ /*
+ * Copy t_self fields back to the caller's original tuples. This does
+ * nothing for untoasted tuples (tuples[i] == heaptuples[i)], but it's
+ * probably faster to always copy than check.
+ */
+ for (i = 0; i < ntuples; i++)
+ tuples[i]->t_self = heaptuples[i]->t_self;
+
+ pgstat_count_heap_insert(relation, ntuples);
+}
+
+/*
+ * Subroutine for heap_lock_updated_tuple_rec.
+ *
+ * Given a hypothetical multixact status held by the transaction identified
+ * with the given xid, does the current transaction need to wait, fail, or can
+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
+ * is set to true if waiting is necessary; if it can continue, then
+ * HeapTupleMayBeUpdated is returned. If the lock is already held by the
+ * current transaction, return HeapTupleSelfUpdated. In case of a conflict
+ * with another transaction, a different HeapTupleSatisfiesUpdate return code
+ * is returned.
+ *
+ * The held status is said to be hypothetical because it might correspond to a
+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
+ * way for simplicity of API.
+ */
+static HTSU_Result
+test_lockmode_for_conflict(MultiXactStatus status, TransactionId xid,
+ LockTupleMode mode, bool *needwait)
+{
+ MultiXactStatus wantedstatus;
+
+ *needwait = false;
+ wantedstatus = get_mxact_status_for_lock(mode, false);
+
+ /*
+ * Note: we *must* check TransactionIdIsInProgress before
+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
+ * explanation.
+ */
+ if (TransactionIdIsCurrentTransactionId(xid))
+ {
+ /*
+ * The tuple has already been locked by our own transaction. This is
+ * very rare but can happen if multiple transactions are trying to
+ * lock an ancient version of the same tuple.
+ */
+ return HeapTupleSelfUpdated;
+ }
+ else if (TransactionIdIsInProgress(xid))
+ {
+ /*
+ * If the locking transaction is running, what we do depends on
+ * whether the lock modes conflict: if they do, then we must wait for
+ * it to finish; otherwise we can fall through to lock this tuple
+ * version without waiting.
+ */
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ {
+ *needwait = true;
+ }
+
+ /*
+ * If we set needwait above, then this value doesn't matter;
+ * otherwise, this value signals to caller that it's okay to proceed.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+ else if (TransactionIdDidAbort(xid))
+ return HeapTupleMayBeUpdated;
+ else if (TransactionIdDidCommit(xid))
+ {
+ /*
+ * The other transaction committed. If it was only a locker, then the
+ * lock is completely gone now and we can return success; but if it
+ * was an update, then what we do depends on whether the two lock
+ * modes conflict. If they conflict, then we must report error to
+ * caller. But if they don't, we can fall through to allow the current
+ * transaction to lock the tuple.
+ *
+ * Note: the reason we worry about ISUPDATE here is because as soon as
+ * a transaction ends, all its locks are gone and meaningless, and
+ * thus we can ignore them; whereas its updates persist. In the
+ * TransactionIdIsInProgress case, above, we don't need to check
+ * because we know the lock is still "alive" and thus a conflict needs
+ * always be checked.
+ */
+ if (!ISUPDATE_from_mxstatus(status))
+ return HeapTupleMayBeUpdated;
+
+ if (DoLockModesConflict(LOCKMODE_from_mxstatus(status),
+ LOCKMODE_from_mxstatus(wantedstatus)))
+ /* bummer */
+ return HeapTupleUpdated;
+
+ return HeapTupleMayBeUpdated;
+ }
+
+ /* Not in progress, not aborted, not committed -- must have crashed */
+ return HeapTupleMayBeUpdated;
+}
+
+
+/*
+ * Recursive part of heap_lock_updated_tuple
+ *
+ * Fetch the tuple pointed to by tid in rel, and mark it as locked by the given
+ * xid with the given mode; if this tuple is updated, recurse to lock the new
+ * version as well.
+ */
+static HTSU_Result
+heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
+ LockTupleMode mode)
+{
+ HTSU_Result result;
+ ItemPointerData tupid;
+ HeapTupleData mytup;
+ Buffer buf;
+ uint16 new_infomask,
+ new_infomask2,
+ old_infomask,
+ old_infomask2;
+ TransactionId xmax,
+ new_xmax;
+ TransactionId priorXmax = InvalidTransactionId;
+ bool cleared_all_frozen = false;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+
+ ItemPointerCopy(tid, &tupid);
+
+ for (;;)
+ {
+ new_infomask = 0;
+ new_xmax = InvalidTransactionId;
+ block = ItemPointerGetBlockNumber(&tupid);
+
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
+ {
+ /*
+ * if we fail to find the updated version of the tuple, it's
+ * because it was vacuumed/pruned away after its creator
+ * transaction aborted. So behave as if we got to the end of the
+ * chain, and there's no further tuple to lock: return success to
+ * caller.
+ */
+ return HeapTupleMayBeUpdated;
+ }
+
+l4:
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it
+ * appears to be necessary. Since we haven't got the lock yet,
+ * someone else might be in the middle of changing this, so we'll need
+ * to recheck after we have the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(rel, block, &vmbuffer);
+ else
+ vmbuffer = InvalidBuffer;
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * If we didn't pin the visibility map page and the page has become
+ * all visible while we were busy locking the buffer, we'll have to
+ * unlock and re-lock, to avoid holding the buffer lock across I/O.
+ * That's a bit unfortunate, but hopefully shouldn't happen often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(BufferGetPage(buf)))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(rel, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ }
+
+ /*
+ * Check the tuple XMIN against prior XMAX, if any. If we reached the
+ * end of the chain, we're done, so return success.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ priorXmax))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /*
+ * Also check Xmin: if this tuple was created by an aborted
+ * (sub)transaction, then we already locked the last live one in the
+ * chain, thus we're done, so return success.
+ */
+ if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ {
+ UnlockReleaseBuffer(buf);
+ return HeapTupleMayBeUpdated;
+ }
+
+ old_infomask = mytup.t_data->t_infomask;
+ old_infomask2 = mytup.t_data->t_infomask2;
+ xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+
+ /*
+ * If this tuple version has been updated or locked by some concurrent
+ * transaction(s), what we do depends on whether our lock mode
+ * conflicts with what those other transactions hold, and also on the
+ * status of them.
+ */
+ if (!(old_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId rawxmax;
+ bool needwait;
+
+ rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ if (old_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int nmembers;
+ int i;
+ MultiXactMember *members;
+
+ /*
+ * We don't need a test for pg_upgrade'd tuples: this is only
+ * applied to tuples after the first in an update chain. Said
+ * first tuple in the chain may well be locked-in-9.2-and-
+ * pg_upgraded, but that one was already locked by our caller,
+ * not us; and any subsequent ones cannot be because our
+ * caller must necessarily have obtained a snapshot later than
+ * the pg_upgrade itself.
+ */
+ Assert(!HEAP_LOCKED_UPGRADED(mytup.t_data->t_infomask));
+
+ nmembers = GetMultiXactIdMembers(rawxmax, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(old_infomask));
+ for (i = 0; i < nmembers; i++)
+ {
+ result = test_lockmode_for_conflict(members[i].status,
+ members[i].xid,
+ mode, &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a
+ * previous iteration of this (say heap_lock_tuple was
+ * forced to restart the locking loop because of a change
+ * in xmax), then we hold the lock already on this tuple
+ * version and we don't need to do anything; and this is
+ * not an error condition either. We just need to skip
+ * this tuple and continue locking the next version in the
+ * update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ {
+ pfree(members);
+ goto next;
+ }
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(members[i].xid, rel,
+ &mytup.t_self,
+ XLTW_LockUpdated);
+ pfree(members);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ pfree(members);
+ goto out_locked;
+ }
+ }
+ if (members)
+ pfree(members);
+ }
+ else
+ {
+ MultiXactStatus status;
+
+ /*
+ * For a non-multi Xmax, we first need to compute the
+ * corresponding MultiXactStatus by using the infomask bits.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(old_infomask))
+ {
+ if (HEAP_XMAX_IS_KEYSHR_LOCKED(old_infomask))
+ status = MultiXactStatusForKeyShare;
+ else if (HEAP_XMAX_IS_SHR_LOCKED(old_infomask))
+ status = MultiXactStatusForShare;
+ else if (HEAP_XMAX_IS_EXCL_LOCKED(old_infomask))
+ {
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusForUpdate;
+ else
+ status = MultiXactStatusForNoKeyUpdate;
+ }
+ else
+ {
+ /*
+ * LOCK_ONLY present alone (a pg_upgraded tuple marked
+ * as share-locked in the old cluster) shouldn't be
+ * seen in the middle of an update chain.
+ */
+ elog(ERROR, "invalid lock status in tuple");
+ }
+ }
+ else
+ {
+ /* it's an update, but which kind? */
+ if (old_infomask2 & HEAP_KEYS_UPDATED)
+ status = MultiXactStatusUpdate;
+ else
+ status = MultiXactStatusNoKeyUpdate;
+ }
+
+ result = test_lockmode_for_conflict(status, rawxmax, mode,
+ &needwait);
+
+ /*
+ * If the tuple was already locked by ourselves in a previous
+ * iteration of this (say heap_lock_tuple was forced to
+ * restart the locking loop because of a change in xmax), then
+ * we hold the lock already on this tuple version and we don't
+ * need to do anything; and this is not an error condition
+ * either. We just need to skip this tuple and continue
+ * locking the next version in the update chain.
+ */
+ if (result == HeapTupleSelfUpdated)
+ goto next;
+
+ if (needwait)
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ XactLockTableWait(rawxmax, rel, &mytup.t_self,
+ XLTW_LockUpdated);
+ goto l4;
+ }
+ if (result != HeapTupleMayBeUpdated)
+ {
+ goto out_locked;
+ }
+ }
+ }
+
+ /* compute the new Xmax and infomask values for the tuple ... */
+ compute_new_xmax_infomask(xmax, old_infomask, mytup.t_data->t_infomask2,
+ xid, mode, false,
+ &new_xmax, &new_infomask, &new_infomask2);
+
+ if (PageIsAllVisible(BufferGetPage(buf)) &&
+ visibilitymap_clear(rel, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+ START_CRIT_SECTION();
+
+ /* ... and set them */
+ HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ mytup.t_data->t_infomask |= new_infomask;
+ mytup.t_data->t_infomask2 |= new_infomask2;
+
+ MarkBufferDirty(buf);
+
+ /* XLOG stuff */
+ if (RelationNeedsWAL(rel))
+ {
+ xl_heap_lock_updated xlrec;
+ XLogRecPtr recptr;
+ Page page = BufferGetPage(buf);
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&mytup.t_self);
+ xlrec.xmax = new_xmax;
+ xlrec.infobits_set = compute_infobits(new_infomask, new_infomask2);
+ xlrec.flags =
+ cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLockUpdated);
+
+ recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_LOCK_UPDATED);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+next:
+ /* if we find the end of update chain, we're done. */
+ if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
+ ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
+ HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_locked;
+ }
+
+ /* tail recursion */
+ priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
+ UnlockReleaseBuffer(buf);
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+ }
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ UnlockReleaseBuffer(buf);
+
+ if (vmbuffer != InvalidBuffer)
+ ReleaseBuffer(vmbuffer);
+
+ return result;
+
+}
+
+/*
+ * heap_lock_updated_tuple
+ * Follow update chain when locking an updated tuple, acquiring locks (row
+ * marks) on the updated versions.
+ *
+ * The initial tuple is assumed to be already locked.
+ *
+ * This function doesn't check visibility, it just unconditionally marks the
+ * tuple(s) as locked. If any tuple in the updated chain is being deleted
+ * concurrently (or updated with the key being modified), sleep until the
+ * transaction doing it is finished.
+ *
+ * Note that we don't acquire heavyweight tuple locks on the tuples we walk
+ * when we have to wait for other transactions to release them, as opposed to
+ * what heap_lock_tuple does. The reason is that having more than one
+ * transaction walking the chain is probably uncommon enough that risk of
+ * starvation is not likely: one of the preconditions for being here is that
+ * the snapshot in use predates the update that created this tuple (because we
+ * started at an earlier version of the tuple), but at the same time such a
+ * transaction cannot be using repeatable read or serializable isolation
+ * levels, because that would lead to a serializability failure.
+ */
+static HTSU_Result
+heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
+ TransactionId xid, LockTupleMode mode)
+{
+ if (!ItemPointerEquals(&tuple->t_self, ctid))
+ {
+ /*
+ * If this is the first possibly-multixact-able operation in the
+ * current transaction, set my per-backend OldestMemberMXactId
+ * setting. We can be certain that the transaction will never become a
+ * member of any older MultiXactIds than that. (We have to do this
+ * even if we end up just using our own TransactionId below, since
+ * some other backend could incorporate our XID into a MultiXact
+ * immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ return heap_lock_updated_tuple_rec(rel, ctid, xid, mode);
+ }
+
+ /* nothing to lock */
+ return HeapTupleMayBeUpdated;
+}
+
+/*
+ * heapam_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+static HTSU_Result
+heapam_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ HTSU_Result result;
+ ItemId lp;
+ Page page;
+ Buffer vmbuffer = InvalidBuffer;
+ BlockNumber block;
+ TransactionId xid,
+ xmax;
+ uint16 old_infomask,
+ new_infomask,
+ new_infomask2;
+ bool first_time = true;
+ bool have_tuple_lock = false;
+ bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+ Buffer buf;
+
+ Assert(stuple != NULL);
+
+ buf = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ block = ItemPointerGetBlockNumber(tid);
+ *buffer = buf;
+
+ /*
+ * Before locking the buffer, pin the visibility map page if it appears to
+ * be necessary. Since we haven't got the lock yet, someone else might be
+ * in the middle of changing this, so we'll need to recheck after we have
+ * the lock.
+ */
+ if (PageIsAllVisible(BufferGetPage(buf)))
+ visibilitymap_pin(relation, block, &vmbuffer);
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ page = BufferGetPage(buf);
+ lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
+ Assert(ItemIdIsNormal(lp));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
+
+l3:
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, buf);
+
+ if (result == HeapTupleInvisible)
+ {
+ /*
+ * This is possible, but only when locking a tuple for ON CONFLICT
+ * UPDATE. We return this value here rather than throwing an error in
+ * order to give that case the opportunity to throw a more specific
+ * error.
+ */
+ result = HeapTupleInvisible;
+ goto out_locked;
+ }
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
+ {
+ TransactionId xwait;
+ uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;
+ ItemPointerData t_ctid;
+
+ /* must copy state data before unlocking buffer */
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
+
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+ /*
+ * If any subtransaction of the current top transaction already holds
+ * a lock as strong as or stronger than what we're requesting, we
+ * effectively hold the desired lock already. We *must* succeed
+ * without trying to take the tuple lock, else we will deadlock
+ * against anyone wanting to acquire a stronger lock.
+ *
+ * Note we only do this the first time we loop on the HTSU result;
+ * there is no point in testing in subsequent passes, because
+ * evidently our own transaction cannot have acquired a new lock after
+ * the first time we checked.
+ */
+ if (first_time)
+ {
+ first_time = false;
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ int i;
+ int nmembers;
+ MultiXactMember *members;
+
+ /*
+ * We don't need to allow old multixacts here; if that had
+ * been the case, HeapTupleSatisfiesUpdate would have returned
+ * MayBeUpdated and we wouldn't be here.
+ */
+ nmembers =
+ GetMultiXactIdMembers(xwait, &members, false,
+ HEAP_XMAX_IS_LOCKED_ONLY(infomask));
+
+ for (i = 0; i < nmembers; i++)
+ {
+ /* only consider members of our own transaction */
+ if (!TransactionIdIsCurrentTransactionId(members[i].xid))
+ continue;
+
+ if (TUPLOCK_from_mxstatus(members[i].status) >= mode)
+ {
+ pfree(members);
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ }
+
+ if (members)
+ pfree(members);
+ }
+ else if (TransactionIdIsCurrentTransactionId(xwait))
+ {
+ switch (mode)
+ {
+ case LockTupleKeyShare:
+ Assert(HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask));
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ case LockTupleShare:
+ if (HEAP_XMAX_IS_SHR_LOCKED(infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleNoKeyExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ case LockTupleExclusive:
+ if (HEAP_XMAX_IS_EXCL_LOCKED(infomask) &&
+ infomask2 & HEAP_KEYS_UPDATED)
+ {
+ result = HeapTupleMayBeUpdated;
+ goto out_unlocked;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initially assume that we will have to wait for the locking
+ * transaction(s) to finish. We check various cases below in which
+ * this can be turned off.
+ */
+ require_sleep = true;
+ if (mode == LockTupleKeyShare)
+ {
+ /*
+ * If we're requesting KeyShare, and there's no update present, we
+ * don't need to wait. Even if there is an update, we can still
+ * continue if the key hasn't been modified.
+ *
+ * However, if there are updates, we need to walk the update chain
+ * to mark future versions of the row as locked, too. That way,
+ * if somebody deletes that future version, we're protected
+ * against the key going away. This locking of future versions
+ * could block momentarily, if a concurrent transaction is
+ * deleting a key; or it could return a value to the effect that
+ * the transaction deleting the key has already committed. So we
+ * do this before re-locking the buffer; otherwise this would be
+ * prone to deadlocks.
+ *
+ * Note that the TID we're locking was grabbed before we unlocked
+ * the buffer. For it to change while we're not looking, the
+ * other properties we're testing for below after re-locking the
+ * buffer would also change, in which case we would restart this
+ * loop above.
+ */
+ if (!(infomask2 & HEAP_KEYS_UPDATED))
+ {
+ bool updated;
+
+ updated = !HEAP_XMAX_IS_LOCKED_ONLY(infomask);
+
+ /*
+ * If there are updates, follow the update chain; bail out if
+ * that cannot be done.
+ */
+ if (follow_updates && updated)
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * Also, if it wasn't updated before we released the lock, but
+ * is updated now, we start over too; the reason is that we
+ * now need to follow the update chain to lock the new
+ * versions.
+ */
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ !updated))
+ goto l3;
+
+ /* Things look okay, so we can skip sleeping */
+ require_sleep = false;
+
+ /*
+ * Note we allow Xmax to change here; other updaters/lockers
+ * could have modified it before we grabbed the buffer lock.
+ * However, this is not a problem, because with the recheck we
+ * just did we ensure that they still don't conflict with the
+ * lock we want.
+ */
+ }
+ }
+ else if (mode == LockTupleShare)
+ {
+ /*
+ * If we're requesting Share, we can similarly avoid sleeping if
+ * there's no update and no exclusive lock present.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(infomask) &&
+ !HEAP_XMAX_IS_EXCL_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * Make sure it's still an appropriate lock, else start over.
+ * See above about allowing xmax to change.
+ */
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
+ goto l3;
+ require_sleep = false;
+ }
+ }
+ else if (mode == LockTupleNoKeyExclusive)
+ {
+ /*
+ * If we're requesting NoKeyExclusive, we might also be able to
+ * avoid sleeping; just ensure that there no conflicting lock
+ * already acquired.
+ */
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
+ mode))
+ {
+ /*
+ * No conflict, but if the xmax changed under us in the
+ * meantime, start over.
+ */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+ else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* if the xmax changed in the meantime, start over */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ /* otherwise, we're good */
+ require_sleep = false;
+ }
+ }
+
+ /*
+ * As a check independent from those above, we can also avoid sleeping
+ * if the current transaction is the sole locker of the tuple. Note
+ * that the strength of the lock already held is irrelevant; this is
+ * not about recording the lock in Xmax (which will be done regardless
+ * of this optimization, below). Also, note that the cases where we
+ * hold a lock stronger than we are requesting are already handled
+ * above by not doing anything.
+ *
+ * Note we only deal with the non-multixact case here; MultiXactIdWait
+ * is well equipped to deal with this situation on its own.
+ */
+ if (require_sleep && !(infomask & HEAP_XMAX_IS_MULTI) &&
+ TransactionIdIsCurrentTransactionId(xwait))
+ {
+ /* ... but if the xmax changed in the meantime, start over */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
+ require_sleep = false;
+ }
+
+ /*
+ * Time to sleep on the other transaction/multixact, if necessary.
+ *
+ * If the other transaction is an update that's already committed,
+ * then sleeping cannot possibly do any good: if we're required to
+ * sleep, get out to raise an error instead.
+ *
+ * By here, we either have already acquired the buffer exclusive lock,
+ * or we must wait for the locking transaction or multixact; so below
+ * we ensure that we grab buffer lock after the sleep.
+ */
+ if (require_sleep && result == HeapTupleUpdated)
+ {
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ else if (require_sleep)
+ {
+ /*
+ * Acquire tuple lock to establish our priority for the tuple, or
+ * die trying. LockTuple will release us when we are next-in-line
+ * for the tuple. We must do this even if we are share-locking.
+ *
+ * If we are forced to "start over" below, we keep the tuple lock;
+ * this arranges that we stay at the head of the line while
+ * rechecking tuple state.
+ */
+ if (!heap_acquire_tuplock(relation, tid, mode, wait_policy,
+ &have_tuple_lock))
+ {
+ /*
+ * This can only happen if wait_policy is Skip and the lock
+ * couldn't be obtained.
+ */
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+
+ if (infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactStatus status = get_mxact_status_for_lock(mode, false);
+
+ /* We only ever lock tuples, never update them */
+ if (status >= MultiXactStatusNoKeyUpdate)
+ elog(ERROR, "invalid lock mode in heap_lock_tuple");
+
+ /* wait for multixact to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ MultiXactIdWait((MultiXactId) xwait, status, infomask,
+ relation, &tuple.t_self, XLTW_Lock, NULL);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
+ status, infomask, relation,
+ NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+
+ break;
+ }
+
+ /*
+ * Of course, the multixact might not be done here: if we're
+ * requesting a light lock mode, other transactions with light
+ * locks could still be alive, as well as locks owned by our
+ * own xact or other subxacts of this backend. We need to
+ * preserve the surviving MultiXact members. Note that it
+ * isn't absolutely necessary in the latter case, but doing so
+ * is simpler.
+ */
+ }
+ else
+ {
+ /* wait for regular transaction to end, or die trying */
+ switch (wait_policy)
+ {
+ case LockWaitBlock:
+ XactLockTableWait(xwait, relation, &tuple.t_self,
+ XLTW_Lock);
+ break;
+ case LockWaitSkip:
+ if (!ConditionalXactLockTableWait(xwait))
+ {
+ result = HeapTupleWouldBlock;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ break;
+ case LockWaitError:
+ if (!ConditionalXactLockTableWait(xwait))
+ ereport(ERROR,
+ (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on row in relation \"%s\"",
+ RelationGetRelationName(relation))));
+ break;
+ }
+ }
+
+ /* if there are updates, follow the update chain */
+ if (follow_updates && !HEAP_XMAX_IS_LOCKED_ONLY(infomask))
+ {
+ HTSU_Result res;
+
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
+ GetCurrentTransactionId(),
+ mode);
+ if (res != HeapTupleMayBeUpdated)
+ {
+ result = res;
+ /* recovery code expects to have buffer lock held */
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto failed;
+ }
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /*
+ * xwait is done, but if xwait had just locked the tuple then some
+ * other xact could update this tuple before we get to this point.
+ * Check for xmax change, and start over if so.
+ */
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
+ xwait))
+ goto l3;
+
+ if (!(infomask & HEAP_XMAX_IS_MULTI))
+ {
+ /*
+ * Otherwise check if it committed or aborted. Note we cannot
+ * be here if the tuple was only locked by somebody who didn't
+ * conflict with us; that would have been handled above. So
+ * that transaction must necessarily be gone by now. But
+ * don't check for this in the multixact case, because some
+ * locker transactions might still be running.
+ */
+ UpdateXmaxHintBits(tuple.t_data, buf, xwait);
+ }
+ }
+
+ /* By here, we're certain that we hold buffer exclusive lock again */
+
+ /*
+ * We may lock if previous xmax aborted, or if it committed but only
+ * locked the tuple without updating it; or if we didn't have to wait
+ * at all for whatever reason.
+ */
+ if (!require_sleep ||
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
+ result = HeapTupleMayBeUpdated;
+ else
+ result = HeapTupleUpdated;
+ }
+
+failed:
+ if (result != HeapTupleMayBeUpdated)
+ {
+ Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
+ result == HeapTupleWouldBlock);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ if (result == HeapTupleSelfUpdated)
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
+ else
+ hufd->cmax = InvalidCommandId;
+ goto out_locked;
+ }
+
+ /*
+ * If we didn't pin the visibility map page and the page has become all
+ * visible while we were busy locking the buffer, or during some
+ * subsequent window during which we had it unlocked, we'll have to unlock
+ * and re-lock, to avoid holding the buffer lock across I/O. That's a bit
+ * unfortunate, especially since we'll now have to recheck whether the
+ * tuple has been locked or updated under us, but hopefully it won't
+ * happen very often.
+ */
+ if (vmbuffer == InvalidBuffer && PageIsAllVisible(page))
+ {
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ visibilitymap_pin(relation, block, &vmbuffer);
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ goto l3;
+ }
+
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
+
+ /*
+ * If this is the first possibly-multixact-able operation in the current
+ * transaction, set my per-backend OldestMemberMXactId setting. We can be
+ * certain that the transaction will never become a member of any older
+ * MultiXactIds than that. (We have to do this even if we end up just
+ * using our own TransactionId below, since some other backend could
+ * incorporate our XID into a MultiXact immediately afterwards.)
+ */
+ MultiXactIdSetOldestMember();
+
+ /*
+ * Compute the new xmax and infomask to store into the tuple. Note we do
+ * not modify the tuple just yet, because that would leave it in the wrong
+ * state if multixact.c elogs.
+ */
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
+ GetCurrentTransactionId(), mode, false,
+ &xid, &new_infomask, &new_infomask2);
+
+ START_CRIT_SECTION();
+
+ /*
+ * Store transaction information of xact locking the tuple.
+ *
+ * Note: Cmax is meaningless in this context, so don't set it; this avoids
+ * possibly generating a useless combo CID. Moreover, if we're locking a
+ * previously updated tuple, it's important to preserve the Cmax.
+ *
+ * Also reset the HOT UPDATE bit, but only if there's no update; otherwise
+ * we would break the HOT chain.
+ */
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
+
+ /*
+ * Make sure there is no forward chain link in t_ctid. Note that in the
+ * cases where the tuple has been updated, we must not overwrite t_ctid,
+ * because it was set by the updater. Moreover, if the tuple has been
+ * updated, we need to follow the update chain to lock the new versions of
+ * the tuple as well.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
+ tuple.t_data->t_ctid = *tid;
+
+ /* Clear only the all-frozen bit on visibility map if needed */
+ if (PageIsAllVisible(page) &&
+ visibilitymap_clear(relation, block, vmbuffer,
+ VISIBILITYMAP_ALL_FROZEN))
+ cleared_all_frozen = true;
+
+
+ MarkBufferDirty(buf);
+
+ /*
+ * XLOG stuff. You might think that we don't need an XLOG record because
+ * there is no state change worth restoring after a crash. You would be
+ * wrong however: we have just written either a TransactionId or a
+ * MultiXactId that may never have been seen on disk before, and we need
+ * to make sure that there are XLOG entries covering those ID numbers.
+ * Else the same IDs might be re-used after a crash, which would be
+ * disastrous if this page made it to disk before the crash. Essentially
+ * we have to enforce the WAL log-before-data rule even in this case.
+ * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
+ * entries for everything anyway.)
+ */
+ if (RelationNeedsWAL(relation))
+ {
+ xl_heap_lock xlrec;
+ XLogRecPtr recptr;
+
+ XLogBeginInsert();
+ XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
+
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
+ xlrec.locking_xid = xid;
+ xlrec.infobits_set = compute_infobits(new_infomask,
+ tuple.t_data->t_infomask2);
+ xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
+ XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
+
+ /* we don't decode row locks atm, so no need to log the origin */
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_LOCK);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+
+ result = HeapTupleMayBeUpdated;
+
+out_locked:
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+
+out_unlocked:
+ if (BufferIsValid(vmbuffer))
+ ReleaseBuffer(vmbuffer);
+
+ /*
+ * Don't update the visibility map here. Locking a tuple doesn't change
+ * visibility info.
+ */
+
+ /*
+ * Now that we have successfully marked the tuple as locked, we can
+ * release the lmgr tuple lock, if we had it.
+ */
+ if (have_tuple_lock)
+ UnlockTupleTuplock(relation, tid, mode);
+
+ *stuple = heap_copytuple(&tuple);
+ return result;
+}
+
+/*
+ * heapam_get_latest_tid - get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to
+ * the passed snapshot. You can pass SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row. Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heapam_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ BlockNumber blk;
+ ItemPointerData ctid;
+ TransactionId priorXmax;
+
+ /* this is to avoid Assert failures on bad input */
+ if (!ItemPointerIsValid(tid))
+ return;
+
+ /*
+ * Since this can be called with user-supplied TID, don't trust the input
+ * too much. (RelationGetNumberOfBlocks is an expensive check, so we
+ * don't check t_ctid links again this way. Note that it would not do to
+ * call it just once and save the result, either.)
+ */
+ blk = ItemPointerGetBlockNumber(tid);
+ if (blk >= RelationGetNumberOfBlocks(relation))
+ elog(ERROR, "block number %u is out of range for relation \"%s\"",
+ blk, RelationGetRelationName(relation));
+
+ /*
+ * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we
+ * need to examine, and *tid is the TID we will return if ctid turns out
+ * to be bogus.
+ *
+ * Note that we will loop until we reach the end of the t_ctid chain.
+ * Depending on the snapshot passed, there might be at most one visible
+ * version of the row, but we don't try to optimize for that.
+ */
+ ctid = *tid;
+ priorXmax = InvalidTransactionId; /* cannot check first XMIN */
+ for (;;)
+ {
+ Buffer buffer;
+ Page page;
+ OffsetNumber offnum;
+ ItemId lp;
+ HeapTupleData tp;
+ bool valid;
+
+ /*
+ * Read, pin, and lock the page.
+ */
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buffer);
+ TestForOldSnapshot(snapshot, relation, page);
+
+ /*
+ * Check for bogus item number. This is not treated as an error
+ * condition because it can happen while following a t_ctid link. We
+ * just assume that the prior tid is OK and return it unchanged.
+ */
+ offnum = ItemPointerGetOffsetNumber(&ctid);
+ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+ lp = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(lp))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /* OK to access the tuple */
+ tp.t_self = ctid;
+ tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tp.t_len = ItemIdGetLength(lp);
+ tp.t_tableOid = RelationGetRelid(relation);
+
+ /*
+ * After following a t_ctid link, we might arrive at an unrelated
+ * tuple. Check for XMIN match.
+ */
+ if (TransactionIdIsValid(priorXmax) &&
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ /*
+ * Check time qualification of tuple; if visible, set it as the new
+ * result candidate.
+ */
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
+ CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+ if (valid)
+ *tid = ctid;
+
+ /*
+ * If there's a valid t_ctid link, follow it, else we're done.
+ */
+ if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+ {
+ UnlockReleaseBuffer(buffer);
+ break;
+ }
+
+ ctid = tp.t_data->t_ctid;
+ priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ UnlockReleaseBuffer(buffer);
+ } /* end of loop */
+}
+
+/*
+ * heapam_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+static void
+heapam_sync(Relation rel)
+{
+ /* non-WAL-logged tables never need fsync */
+ if (!RelationNeedsWAL(rel))
+ return;
+
+ /* main heap */
+ FlushRelationBuffers(rel);
+ /* FlushRelationBuffers will have opened rd_smgr */
+ smgrimmedsync(rel->rd_smgr, MAIN_FORKNUM);
+
+ /* FSM is not critical, don't bother syncing it */
+
+ /* toast heap, if any */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ toastrel = heap_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+ FlushRelationBuffers(toastrel);
+ smgrimmedsync(toastrel->rd_smgr, MAIN_FORKNUM);
+ heap_close(toastrel, AccessShareLock);
+ }
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data) HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data) HeapTupleHeaderGetUpdateXid(((HeapTuple) tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data) HeapTupleHeaderGetCmin(((HeapTuple) tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data) ((HeapTuple) tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data) ((HeapTuple) tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heapam_lock_tuple;
+ amroutine->multi_insert = heapam_multi_insert;
+
+ amroutine->speculative_abort = heapam_abort_speculative;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heapam_get_latest_tid;
+
+ amroutine->relation_sync = heapam_sync;
amroutine->snapshot_satisfies = HeapTupleSatisfies;
amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 3118c2d33f..84ac4bfade 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -111,6 +111,7 @@
#include "access/heapam_common.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -127,13 +128,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -358,7 +359,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/storageam.c b/src/backend/access/heap/storageam.c
new file mode 100644
index 0000000000..870490d00f
--- /dev/null
+++ b/src/backend/access/heap/storageam.c
@@ -0,0 +1,281 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/storageam.c
+ *
+ *
+ * NOTES
+ * This file contains the storage_ routines which implement
+ * the POSTGRES storage access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/relscan.h"
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "access/tuptoaster.h"
+#include "access/valid.h"
+#include "access/visibilitymap.h"
+#include "access/xloginsert.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ *
+ * On entry, tuple->t_self is the TID to fetch. We pin the buffer holding
+ * the tuple, fill in the remaining fields of *tuple, and check the tuple
+ * against the specified snapshot.
+ *
+ * If successful (tuple found and passes snapshot time qual), then *userbuf
+ * is set to the buffer holding the tuple and TRUE is returned. The caller
+ * must unpin the buffer when done with the tuple.
+ *
+ * If the tuple is not found (ie, item number references a deleted slot),
+ * then tuple->t_data is set to NULL and FALSE is returned.
+ *
+ * If the tuple is found but fails the time qual check, then FALSE is returned
+ * but tuple->t_data is left pointing to the tuple.
+ *
+ * keep_buf determines what is done with the buffer in the FALSE-result cases.
+ * When the caller specifies keep_buf = true, we retain the pin on the buffer
+ * and return it in *userbuf (so the caller must eventually unpin it); when
+ * keep_buf = false, the pin is released and *userbuf is set to InvalidBuffer.
+ *
+ * stats_relation is the relation to charge the heap_fetch operation against
+ * for statistical purposes. (This could be the heap rel itself, an
+ * associated index, or NULL to not count the fetch at all.)
+ *
+ * heap_fetch does not follow HOT chains: only the exact TID requested will
+ * be fetched.
+ *
+ * It is somewhat inconsistent that we ereport() on invalid block number but
+ * return false on invalid item number. There are a couple of reasons though.
+ * One is that the caller can relatively easily check the block number for
+ * validity, but cannot check the item number without reading the page
+ * himself. Another is that when we are following a t_ctid link, we can be
+ * reasonably confident that the page number is valid (since VACUUM shouldn't
+ * truncate off the destination page without having killed the referencing
+ * tuple first), but the item number might well not be good.
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ *
+ * Note that this acquires a buffer pin, which the caller must release.
+ *
+ * Input parameters:
+ * relation: relation containing tuple (caller must hold suitable lock)
+ * tuple->t_self: TID of tuple to lock (rest of struct need not be valid)
+ * cid: current command ID (used for visibility test, and stored into
+ * tuple's cmax if lock is successful)
+ * mode: indicates if shared or exclusive tuple lock is desired
+ * wait_policy: what to do if tuple lock is not available
+ * follow_updates: if true, follow the update chain to also lock descendant
+ * tuples.
+ *
+ * Output parameters:
+ * *tuple: all fields filled in
+ * *buffer: set to buffer holding tuple (pinned but not locked at exit)
+ * *hufd: filled in failure cases (see below)
+ *
+ * Function result may be:
+ * HeapTupleMayBeUpdated: lock was successfully acquired
+ * HeapTupleInvisible: lock failed because tuple was never visible to us
+ * HeapTupleSelfUpdated: lock failed because tuple updated by self
+ * HeapTupleUpdated: lock failed because tuple updated by other xact
+ * HeapTupleWouldBlock: lock couldn't be acquired and wait_policy is skip
+ *
+ * In the failure cases other than HeapTupleInvisible, the routine fills
+ * *hufd with the tuple's t_ctid, t_xmax (resolving a possible MultiXact,
+ * if necessary), and t_cmax (the last only for HeapTupleSelfUpdated,
+ * since we cannot obtain cmax from a combocid generated by another
+ * transaction).
+ * See comments for struct HeapUpdateFailureData for additional info.
+ *
+ * See README.tuplock for a thorough explanation of this mechanism.
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid, options,
+ bistate, IndexFunc, estate,
+ arbiterIndexes, recheckIndexes);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, estate,
+ cid, crosscheck, wait, hufd,
+ lockmode, IndexFunc, recheckIndexes);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ *
+ * This is like heap_insert(), but inserts multiple tuples in one operation.
+ * That's faster than calling heap_insert() in a loop, because when multiple
+ * tuples can be inserted on a single page, we can write just a single WAL
+ * record covering all of them, and only need to lock/unlock the page once.
+ *
+ * Note: this leaks memory into the current memory context. You can create a
+ * temporary context before calling this, if that's a problem.
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ *
+ * Marks a tuple that was speculatively inserted in the same command as dead,
+ * by setting its xmin as invalid. That makes it immediately appear as dead
+ * to all transactions, including our own. In particular, it makes
+ * HeapTupleSatisfiesDirty() regard the tuple as dead, so that another backend
+ * inserting a duplicate key value won't unnecessarily wait for our whole
+ * transaction to finish (it'll just wait for our speculative insertion to
+ * finish).
+ *
+ * Killing the tuple prevents "unprincipled deadlocks", which are deadlocks
+ * that arise due to a mutual dependency that is not user visible. By
+ * definition, unprincipled deadlocks cannot be prevented by the user
+ * reordering lock acquisition in client code, because the implementation level
+ * lock acquisitions are not under the user's direct control. If speculative
+ * inserters did not take this precaution, then under high concurrency they
+ * could deadlock with each other, which would not be acceptable.
+ *
+ * This is somewhat redundant with heap_delete, but we prefer to have a
+ * dedicated routine with stripped down requirements. Note that this is also
+ * used to delete the TOAST tuples created during speculative insertion.
+ *
+ * This routine does not affect logical decoding as it only looks at
+ * confirmation records.
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ *
+ * This forces the heap contents (including TOAST heap if any) down to disk.
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
+ * relation down to disk before it's safe to commit the transaction. This
+ * requires writing out any dirty buffers and then doing a forced fsync.
+ *
+ * Indexes are not touched. (Currently, index operations associated with
+ * the commands that use this are WAL-logged and so do not need fsync.
+ * That behavior might change someday, but in any case it's likely that
+ * any fsync decisions required would be per-index and hence not appropriate
+ * to be done here.)
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 5a8f1dab83..ba9592d055 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,13 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 96cda7091b..ad28b3af3e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -20,6 +20,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2716,8 +2717,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2780,19 +2779,11 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
- estate,
- false,
- NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options,
+ bistate, ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2888,7 +2879,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2921,12 +2912,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2942,10 +2933,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
recheckIndexes =
- ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
- estate, false, NULL, NIL);
+ ExecInsertIndexTuples(myslot, estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2962,8 +2952,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 213a8cccbc..9e6fb8740b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,28 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
-
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ slot->tts_tupleOid = InvalidOid;
+
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +623,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740e28..936ea9b9e5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,19 +492,22 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
-
- heap_insert(myState->transientrel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ ExecMaterializeSlot(slot);
+
+ storage_insert(myState->transientrel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -522,7 +526,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c902293741..458c897741 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4663,7 +4664,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate,
+ NULL, NULL, NIL, NULL);
ResetExprContext(econtext);
@@ -4687,7 +4689,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 005bdbd023..680a65cad5 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2352,17 +2352,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3012,9 +3016,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3030,11 +3035,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, LockWaitBlock,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, &tuple,
+ estate->es_output_cid,
+ lockmode, LockWaitBlock,
+ false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3066,7 +3071,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3112,6 +3118,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3130,17 +3137,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3946,8 +3953,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -4020,10 +4027,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -4037,10 +4043,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189fa71..ab533cf9c7 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -269,12 +269,12 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
*/
List *
ExecInsertIndexTuples(TupleTableSlot *slot,
- ItemPointer tupleid,
EState *estate,
bool noDupErr,
bool *specConflict,
List *arbiterIndexes)
{
+ ItemPointer tupleid = &slot->tts_tid;
List *result = NIL;
ResultRelInfo *resultRelInfo;
int i;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 2e8aca59a7..569566cc92 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1894,7 +1894,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1974,7 +1974,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2021,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2480,7 +2480,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2497,7 +2498,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2528,7 +2531,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2561,14 +2564,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2576,12 +2579,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2595,7 +2598,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -2617,7 +2620,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2646,20 +2650,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, wait_policy,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, tuple,
+ estate->es_output_cid,
+ lockmode, wait_policy,
+ false, &buffer, &hufd);
/* We now have two pins on the buffer, get rid of one */
ReleaseBuffer(buffer);
@@ -2695,12 +2702,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2722,10 +2732,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2734,7 +2740,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2743,7 +2749,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple)tuple)->t_data),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -2762,7 +2768,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2770,17 +2778,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2826,7 +2836,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2845,7 +2855,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2873,7 +2883,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2904,8 +2914,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2929,11 +2937,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
- erm,
- datum,
- &updated);
- if (copyTuple == NULL)
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ erm,
+ datum,
+ &updated);
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2947,23 +2955,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2973,19 +2976,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3151,8 +3147,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 07640a9992..24b5ff7298 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,6 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -354,19 +355,12 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can inspect. */
- tuple = ExecHeapifySlot(slot);
-
- /* OK, store the tuple and create index entries for it */
- simple_heap_insert(rel, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot,
+ GetCurrentCommandId(true), 0, NULL,
+ ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +384,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -415,22 +409,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ HeapUpdateFailureData hufd;
+ LockTupleMode lockmode;
+ InsertIndexTuples IndexFunc = ExecInsertIndexTuples;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can write. */
- tuple = ExecHeapifySlot(slot);
+ storage_update(rel, tid, slot, estate, GetCurrentCommandId(true), InvalidSnapshot,
+ true, &hufd, &lockmode, IndexFunc, &recheckIndexes);
- /* OK, update the tuple and index entries for it */
- simple_heap_update(rel, tid, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, tid,
- estate, false, NULL,
- NIL);
+ tuple = ExecHeapifySlot(slot);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 93aeb3eb6e..d618b3d903 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -83,6 +83,7 @@
#include "access/heapam_common.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..2da5240d24 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple) (&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
- estate->es_output_cid,
- lockmode, erm->waitPolicy, true,
- &buffer, &hufd);
- ReleaseBuffer(buffer);
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
+ estate->es_output_cid,
+ lockmode, erm->waitPolicy, true,
+ &buffer, &hufd);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index ec5c543bdd..965c629870 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
@@ -164,15 +167,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -191,7 +192,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -204,13 +205,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -226,19 +229,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
- Relation rel = relinfo->ri_RelationDesc;
Buffer buffer;
- HeapTupleData tuple;
+ Relation rel = relinfo->ri_RelationDesc;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -259,7 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -267,12 +271,6 @@ ExecInsert(ModifyTableState *mtstate,
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
/*
* get information on the (current) result relation
*/
@@ -284,6 +282,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -374,19 +374,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where
+ * the plan tree can return a tuple extracted literally from some table
+ * with the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -404,9 +416,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -418,9 +427,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -436,14 +442,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -463,7 +467,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -493,7 +498,6 @@ ExecInsert(ModifyTableState *mtstate,
/* Perform a speculative insertion. */
uint32 specToken;
ItemPointerData conflictTid;
- bool specConflict;
/*
* Do a non-conclusive check for conflicts first.
@@ -508,7 +512,7 @@ ExecInsert(ModifyTableState *mtstate,
* speculatively.
*/
vlock:
- specConflict = false;
+ slot->tts_specConflict = false;
if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
arbiterIndexes))
{
@@ -554,24 +558,17 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- HEAP_INSERT_SPECULATIVE,
- NULL);
-
- /* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, true, &specConflict,
- arbiterIndexes);
-
- /* adjust the tuple's state accordingly */
- if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
- else
- heap_abort_speculative(resultRelationDesc, tuple);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ HEAP_INSERT_SPECULATIVE,
+ NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -587,7 +584,7 @@ ExecInsert(ModifyTableState *mtstate,
* the pre-check again, which will now find the conflicting tuple
* (unless it aborts before we get there).
*/
- if (specConflict)
+ if (slot->tts_specConflict)
{
list_free(recheckIndexes);
goto vlock;
@@ -599,19 +596,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- 0, NULL);
-
- /* insert index entries for tuple */
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- arbiterIndexes);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ 0, NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
}
}
@@ -619,11 +611,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -674,7 +666,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -719,8 +711,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -746,8 +736,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -761,11 +753,11 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd);
+ result = storage_delete(resultRelationDesc, tupleid,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -861,7 +853,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -875,20 +867,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -897,7 +888,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -934,14 +925,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1006,14 +997,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1023,7 +1014,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1059,11 +1050,14 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd, &lockmode);
+ result = storage_update(resultRelationDesc, tupleid, slot,
+ estate,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd, &lockmode,
+ ExecInsertIndexTuples,
+ &recheckIndexes);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -1134,26 +1128,6 @@ lreplace:;
elog(ERROR, "unrecognized heap_update status: %u", result);
return NULL;
}
-
- /*
- * Note: instead of having to update the old index tuples associated
- * with the heap tuple, all we do is form and insert new index tuples.
- * This is because UPDATEs are actually DELETEs and INSERTs, and index
- * tuple deletion is done later by VACUUM (see notes in ExecDelete).
- * All we do here is insert new index tuples. -cim 9/27/89
- */
-
- /*
- * insert index entries for tuple
- *
- * Note: heap_update returns the tid (location) of the new tuple in
- * the t_self field.
- *
- * If it's a HOT update, we mustn't insert new index entries.
- */
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL, NIL);
}
if (canSetTag)
@@ -1211,11 +1185,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1226,10 +1201,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1254,7 +1227,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1285,7 +1259,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1313,10 +1289,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1331,7 +1307,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1371,12 +1349,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1579,7 +1559,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..47d8b7b12f 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -331,12 +332,6 @@ TidNext(TidScanState *node)
tidList = node->tss_TidList;
numTids = node->tss_NumTids;
- /*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
/*
* Initialize or advance scan position, depending on direction.
*/
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ /* hari */
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 854097dd58..c4481ecee9 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -352,7 +353,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -387,7 +388,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024e92..cdd45ef313 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -133,40 +133,19 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
- HeapTuple tuple, Buffer *userbuf, bool keep_buf,
- Relation stats_relation);
+extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
+ int options, BulkInsertState bistate);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
-extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
- ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
extern BulkInsertState GetBulkInsertState(void);
extern void FreeBulkInsertState(BulkInsertState);
extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
-extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
- int options, BulkInsertState bistate);
-extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
- CommandId cid, int options, BulkInsertState bistate);
-extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
-extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
- HeapTuple newtup,
- CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
- CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
- bool follow_update,
- Buffer *buffer, HeapUpdateFailureData *hufd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -179,7 +158,6 @@ extern void simple_heap_delete(Relation relation, ItemPointer tid);
extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
-extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
/* in heap/pruneheap.c */
diff --git a/src/include/access/heapam_common.h b/src/include/access/heapam_common.h
index feb35c5024..14aad60546 100644
--- a/src/include/access/heapam_common.h
+++ b/src/include/access/heapam_common.h
@@ -34,6 +34,111 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
+/*
+ * Each tuple lock mode has a corresponding heavyweight lock, and one or two
+ * corresponding MultiXactStatuses (one to merely lock tuples, another one to
+ * update them). This table (and the macros below) helps us determine the
+ * heavyweight lock mode and MultiXactStatus values to use for any particular
+ * tuple lock strength.
+ *
+ * Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
+ * instead.
+ */
+static const struct
+{
+ LOCKMODE hwlock;
+ int lockstatus;
+ int updstatus;
+}
+
+ tupleLockExtraInfo[MaxLockTupleMode + 1] =
+{
+ { /* LockTupleKeyShare */
+ AccessShareLock,
+ MultiXactStatusForKeyShare,
+ -1 /* KeyShare does not allow updating tuples */
+ },
+ { /* LockTupleShare */
+ RowShareLock,
+ MultiXactStatusForShare,
+ -1 /* Share does not allow updating tuples */
+ },
+ { /* LockTupleNoKeyExclusive */
+ ExclusiveLock,
+ MultiXactStatusForNoKeyUpdate,
+ MultiXactStatusNoKeyUpdate
+ },
+ { /* LockTupleExclusive */
+ AccessExclusiveLock,
+ MultiXactStatusForUpdate,
+ MultiXactStatusUpdate
+ }
+};
+
+/*
+ * This table maps tuple lock strength values for each particular
+ * MultiXactStatus value.
+ */
+static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
+{
+ LockTupleKeyShare, /* ForKeyShare */
+ LockTupleShare, /* ForShare */
+ LockTupleNoKeyExclusive, /* ForNoKeyUpdate */
+ LockTupleExclusive, /* ForUpdate */
+ LockTupleNoKeyExclusive, /* NoKeyUpdate */
+ LockTupleExclusive /* Update */
+};
+
+/* Get the LockTupleMode for a given MultiXactStatus */
+#define TUPLOCK_from_mxstatus(status) \
+ (MultiXactStatusLock[(status)])
+
+/*
+ * Acquire heavyweight locks on tuples, using a LockTupleMode strength value.
+ * This is more readable than having every caller translate it to lock.h's
+ * LOCKMODE.
+ */
+#define LockTupleTuplock(rel, tup, mode) \
+ LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define UnlockTupleTuplock(rel, tup, mode) \
+ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+#define ConditionalLockTupleTuplock(rel, tup, mode) \
+ ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
+/* Get the LOCKMODE for a given MultiXactStatus */
+#define LOCKMODE_from_mxstatus(status) \
+ (tupleLockExtraInfo[TUPLOCK_from_mxstatus((status))].hwlock)
+extern HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
+ TransactionId xid, CommandId cid, int options);
+
+extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
+ CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+
+extern XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
+extern uint8 compute_infobits(uint16 infomask, uint16 infomask2);
+extern void compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ uint16 old_infomask2, TransactionId add_to_xmax,
+ LockTupleMode mode, bool is_update,
+ TransactionId *result_xmax, uint16 *result_infomask,
+ uint16 *result_infomask2);
+extern void UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid);
+extern bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
+ LockTupleMode lockmode);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+ uint16 infomask, Relation rel, int *remaining);
+
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
+ Relation rel, ItemPointer ctid, XLTW_Oper oper,
+ int *remaining);
+extern MultiXactStatus get_mxact_status_for_lock(LockTupleMode mode, bool is_update);
+
+extern void heap_inplace_update(Relation relation, HeapTuple tuple);
+extern bool heap_acquire_tuplock(Relation relation, ItemPointer tid,
+ LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool *have_tuple_lock);
/* in heap/heapam_common.c */
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
@@ -43,6 +148,28 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
typedef struct StorageSlotAmRoutine StorageSlotAmRoutine;
extern StorageSlotAmRoutine * heapam_storage_slot_handler(void);
+
+/*
+ * Given two versions of the same t_infomask for a tuple, compare them and
+ * return whether the relevant status for a tuple Xmax has changed. This is
+ * used after a buffer lock has been released and reacquired: we want to ensure
+ * that the tuple state continues to be the same it was when we previously
+ * examined it.
+ *
+ * Note the Xmax field itself must be compared separately.
+ */
+static inline bool
+xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
+{
+ const uint16 interesting =
+ HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY | HEAP_LOCK_MASK;
+
+ if ((new_infomask & interesting) != (old_infomask & interesting))
+ return true;
+
+ return false;
+}
+
/*
* SetHintBits()
*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 168edb058d..489aa78731 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -816,6 +816,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000000..447935ba10
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "executor/tuptable.h"
+#include "nodes/execnodes.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+
+/* Function pointer to let the index tuple insert from storage am */
+typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 90b16ccd0a..6e790bb21a 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -15,30 +15,11 @@
#include "access/heapam.h"
#include "access/sdir.h"
#include "access/skey.h"
+#include "access/storageam.h"
#include "executor/tuptable.h"
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-
typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
@@ -70,11 +51,9 @@ typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
bool *all_dead, bool first_call);
-typedef Oid (*TupleInsert_function) (Relation relation,
- TupleTableSlot *tupslot,
- CommandId cid,
- int options,
- BulkInsertState bistate);
+typedef Oid (*TupleInsert_function) (Relation rel, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
typedef HTSU_Result (*TupleDelete_function) (Relation relation,
ItemPointer tid,
@@ -86,11 +65,14 @@ typedef HTSU_Result (*TupleDelete_function) (Relation relation,
typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
ItemPointer otid,
TupleTableSlot *slot,
+ EState *estate,
CommandId cid,
Snapshot crosscheck,
bool wait,
HeapUpdateFailureData *hufd,
- LockTupleMode *lockmode);
+ LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc,
+ List **recheckIndexes);
typedef bool (*TupleFetch_function) (Relation relation,
ItemPointer tid,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index adbcfa1297..203371148c 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index c4ecf0d50f..85b1e15093 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -196,16 +196,16 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
- int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
- TransactionId priorXmax);
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
+ int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
+ TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
extern void ExecSetupPartitionTupleRouting(Relation rel,
Index resultRTindex,
EState *estate,
@@ -528,9 +528,8 @@ extern int ExecCleanTargetListLength(List *targetlist);
*/
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
-extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate, bool noDupErr, bool *specConflict,
- List *arbiterIndexes);
+extern List *ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
ItemPointer conflictTid, List *arbiterIndexes);
extern void check_exclusion_constraint(Relation heap, Relation index,
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index a730f265dd..630899ea22 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -133,6 +133,7 @@ typedef struct TupleTableSlot
Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_specConflict; /* XXX describe */
bool tts_shouldFree;
bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d209ec012c..9d1549115d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -503,7 +503,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -1751,7 +1751,7 @@ typedef struct SharedSortInfo
{
int num_workers;
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
-} SharedSortInfo;
+} SharedSortInfo;
/* ----------------
* SortState information
@@ -2033,7 +2033,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index b1c94de683..1a403b8e21 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -14,7 +14,6 @@
#ifndef BUFMGR_H
#define BUFMGR_H
-#include "access/storageamapi.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/bufpage.h"
--
2.14.2.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-11-14 08:09 ` Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Michael Paquier @ 2017-11-14 08:09 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Nov 7, 2017 at 6:34 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:
> On Tue, Oct 31, 2017 at 8:59 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>> Additional changes that are done in the patches compared to earlier
>> patches apart from rebase.
>
> Rebased patches are attached.
This set of patches needs again a... Rebase.
--
Michael
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
@ 2017-11-29 04:50 ` Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Michael Paquier @ 2017-11-29 04:50 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Nov 14, 2017 at 5:09 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
> On Tue, Nov 7, 2017 at 6:34 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:
>> On Tue, Oct 31, 2017 at 8:59 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
>> wrote:
>>> Additional changes that are done in the patches compared to earlier
>>> patches apart from rebase.
>>
>> Rebased patches are attached.
>
> This set of patches needs again a... Rebase.
No rebased versions have showed up for two weeks. For now I am marking
this patch as returned with feedback.
--
Michael
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
@ 2017-12-12 04:06 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2017-12-12 04:06 UTC (permalink / raw)
To: Michael Paquier <michael.paquier@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Nov 29, 2017 at 3:50 PM, Michael Paquier <michael.paquier@gmail.com>
wrote:
> On Tue, Nov 14, 2017 at 5:09 PM, Michael Paquier
> <michael.paquier@gmail.com> wrote:
> > On Tue, Nov 7, 2017 at 6:34 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
> >> On Tue, Oct 31, 2017 at 8:59 PM, Haribabu Kommi <
> kommi.haribabu@gmail.com>
> >> wrote:
> >>> Additional changes that are done in the patches compared to earlier
> >>> patches apart from rebase.
> >>
> >> Rebased patches are attached.
> >
> > This set of patches needs again a... Rebase.
>
> No rebased versions have showed up for two weeks. For now I am marking
> this patch as returned with feedback.
>
I restructured that patch files to avoid showing unnecessary modifications,
and also it will be easy for adding of new API's based on the all the
functions
that are exposed by heapam module easily compared earlier.
Attached are the latest set of patches. I will work on the remaining pending
items.
Regards,
Hari Babu
Fujitsu Australia
Attachments:
[application/octet-stream] 0008-Remove-HeapScanDesc-usage-outside-heap.patch (95.6K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/3-0008-Remove-HeapScanDesc-usage-outside-heap.patch)
download | inline diff:
From 50490821517164ac55699789df7e733f8ffb95c3 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Mon, 11 Dec 2017 22:00:09 +1100
Subject: [PATCH 8/8] Remove HeapScanDesc usage outside heap
HeapScanDesc is divided into two scan descriptors.
StorageScanDesc and HeapPageScanDesc.
StorageScanDesc has common members that are should
be available across all the storage routines and
HeapPageScanDesc is avaiable only for the storage
routine that supports Heap storage with page format.
The HeapPageScanDesc is used internally by the heapam
storage routine and also this is exposed to Bitmap Heap
and Sample scan's as they depend on the Heap page format.
while generating the Bitmap Heap and Sample scan's,
the planner now checks whether the storage routine
supports returning HeapPageScanDesc or not? Based on
this decision, the planner plans above two plans.
---
contrib/pgrowlocks/pgrowlocks.c | 4 +-
contrib/pgstattuple/pgstattuple.c | 10 +-
contrib/tsm_system_rows/tsm_system_rows.c | 18 +-
contrib/tsm_system_time/tsm_system_time.c | 8 +-
src/backend/access/heap/heapam.c | 424 +++++++++++++++--------------
src/backend/access/heap/heapam_storage.c | 54 ++++
src/backend/access/index/genam.c | 4 +-
src/backend/access/storage/storageam.c | 51 +++-
src/backend/access/tablesample/system.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/aclchk.c | 4 +-
src/backend/catalog/index.c | 8 +-
src/backend/catalog/partition.c | 2 +-
src/backend/catalog/pg_conversion.c | 2 +-
src/backend/catalog/pg_db_role_setting.c | 2 +-
src/backend/catalog/pg_publication.c | 2 +-
src/backend/catalog/pg_subscription.c | 2 +-
src/backend/commands/cluster.c | 4 +-
src/backend/commands/copy.c | 2 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 10 +-
src/backend/commands/tablespace.c | 10 +-
src/backend/commands/typecmds.c | 4 +-
src/backend/commands/vacuum.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 69 +++--
src/backend/executor/nodeSamplescan.c | 50 ++--
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/optimizer/util/plancat.c | 4 +-
src/backend/postmaster/autovacuum.c | 4 +-
src/backend/postmaster/pgstat.c | 2 +-
src/backend/replication/logical/launcher.c | 2 +-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/utils/init/postinit.c | 2 +-
src/include/access/heapam.h | 36 +--
src/include/access/relscan.h | 47 ++--
src/include/access/storage_common.h | 1 -
src/include/access/storageam.h | 44 +--
src/include/access/storageamapi.h | 42 +--
src/include/nodes/execnodes.h | 4 +-
40 files changed, 536 insertions(+), 421 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index bc8b423975..762d969ecd 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -56,7 +56,7 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
typedef struct
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
int ncolumns;
} MyData;
@@ -71,7 +71,7 @@ Datum
pgrowlocks(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index c4b10d6efc..32ac121e92 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -314,7 +314,8 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
static Datum
pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
HeapTuple tuple;
BlockNumber nblocks;
BlockNumber block = 0; /* next block to count free space in */
@@ -328,7 +329,8 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
- nblocks = scan->rs_nblocks; /* # blocks to be scanned */
+ pagescan = storageam_get_heappagescandesc(scan);
+ nblocks = pagescan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
@@ -364,7 +366,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -377,7 +379,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, scan->rs_strategy);
+ RBM_NORMAL, pagescan->rs_strategy);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 544458ec91..14120291d0 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -71,7 +71,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node);
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
-static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan);
+static bool SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
@@ -209,7 +209,7 @@ static BlockNumber
system_rows_nextsampleblock(SampleScanState *node)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
/* First call within scan? */
if (sampler->doneblocks == 0)
@@ -221,14 +221,14 @@ system_rows_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -258,7 +258,7 @@ system_rows_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
@@ -278,7 +278,7 @@ system_rows_nextsampletuple(SampleScanState *node,
OffsetNumber maxoffset)
{
SystemRowsSamplerData *sampler = (SystemRowsSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
OffsetNumber tupoffset = sampler->lt;
/* Quit if we've returned all needed tuples */
@@ -291,7 +291,7 @@ system_rows_nextsampletuple(SampleScanState *node,
*/
/* We rely on the data accumulated in pagemode access */
- Assert(scan->rs_pageatatime);
+ Assert(pagescan->rs_pageatatime);
for (;;)
{
/* Advance to next possible offset on page */
@@ -308,7 +308,7 @@ system_rows_nextsampletuple(SampleScanState *node,
}
/* Found a candidate? */
- if (SampleOffsetVisible(tupoffset, scan))
+ if (SampleOffsetVisible(tupoffset, pagescan))
{
sampler->donetuples++;
break;
@@ -327,7 +327,7 @@ system_rows_nextsampletuple(SampleScanState *node,
* so just look at the info it left in rs_vistuples[].
*/
static bool
-SampleOffsetVisible(OffsetNumber tupoffset, HeapScanDesc scan)
+SampleOffsetVisible(OffsetNumber tupoffset, HeapPageScanDesc scan)
{
int start = 0,
end = scan->rs_ntuples - 1;
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index af8d025414..aa7252215a 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -219,7 +219,7 @@ static BlockNumber
system_time_nextsampleblock(SampleScanState *node)
{
SystemTimeSamplerData *sampler = (SystemTimeSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
instr_time cur_time;
/* First call within scan? */
@@ -232,14 +232,14 @@ system_time_nextsampleblock(SampleScanState *node)
SamplerRandomState randstate;
/* If relation is empty, there's nothing to scan */
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
return InvalidBlockNumber;
/* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate);
/* Compute nblocks/firstblock/step only once per query */
- sampler->nblocks = scan->rs_nblocks;
+ sampler->nblocks = pagescan->rs_nblocks;
/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
@@ -275,7 +275,7 @@ system_time_nextsampleblock(SampleScanState *node)
{
/* Advance lb, using uint64 arithmetic to forestall overflow */
sampler->lb = ((uint64) sampler->lb + sampler->step) % sampler->nblocks;
- } while (sampler->lb >= scan->rs_nblocks);
+ } while (sampler->lb >= pagescan->rs_nblocks);
return sampler->lb;
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 4565e1c933..4d4a632354 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -220,9 +220,9 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* lock that ensures the interesting tuple(s) won't change.)
*/
if (scan->rs_parallel != NULL)
- scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+ scan->rs_pagescan.rs_nblocks = scan->rs_parallel->phs_nblocks;
else
- scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+ scan->rs_pagescan.rs_nblocks = RelationGetNumberOfBlocks(scan->rs_scan.rs_rd);
/*
* If the table is large relative to NBuffers, use a bulk-read access
@@ -236,8 +236,8 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* Note that heap_parallelscan_initialize has a very similar test; if you
* change this, consider changing that one, too.
*/
- if (!RelationUsesLocalBuffers(scan->rs_rd) &&
- scan->rs_nblocks > NBuffers / 4)
+ if (!RelationUsesLocalBuffers(scan->rs_scan.rs_rd) &&
+ scan->rs_pagescan.rs_nblocks > NBuffers / 4)
{
allow_strat = scan->rs_allow_strat;
allow_sync = scan->rs_allow_sync;
@@ -248,20 +248,20 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (allow_strat)
{
/* During a rescan, keep the previous strategy object. */
- if (scan->rs_strategy == NULL)
- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);
+ if (scan->rs_pagescan.rs_strategy == NULL)
+ scan->rs_pagescan.rs_strategy = GetAccessStrategy(BAS_BULKREAD);
}
else
{
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
- scan->rs_strategy = NULL;
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
+ scan->rs_pagescan.rs_strategy = NULL;
}
if (scan->rs_parallel != NULL)
{
/* For parallel scan, believe whatever ParallelHeapScanDesc says. */
- scan->rs_syncscan = scan->rs_parallel->phs_syncscan;
+ scan->rs_pagescan.rs_syncscan = scan->rs_parallel->phs_syncscan;
}
else if (keep_startblock)
{
@@ -270,25 +270,25 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
- scan->rs_syncscan = (allow_sync && synchronize_seqscans);
+ scan->rs_pagescan.rs_syncscan = (allow_sync && synchronize_seqscans);
}
else if (allow_sync && synchronize_seqscans)
{
- scan->rs_syncscan = true;
- scan->rs_startblock = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ scan->rs_pagescan.rs_syncscan = true;
+ scan->rs_pagescan.rs_startblock = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
}
else
{
- scan->rs_syncscan = false;
- scan->rs_startblock = 0;
+ scan->rs_pagescan.rs_syncscan = false;
+ scan->rs_pagescan.rs_startblock = 0;
}
- scan->rs_numblocks = InvalidBlockNumber;
- scan->rs_inited = false;
+ scan->rs_pagescan.rs_numblocks = InvalidBlockNumber;
+ scan->rs_scan.rs_inited = false;
scan->rs_ctup.t_data = NULL;
ItemPointerSetInvalid(&scan->rs_ctup.t_self);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
/* page-at-a-time fields are always invalid when not rs_inited */
@@ -296,7 +296,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* copy the scan key, if appropriate
*/
if (key != NULL)
- memcpy(scan->rs_key, key, scan->rs_nkeys * sizeof(ScanKeyData));
+ memcpy(scan->rs_scan.rs_key, key, scan->rs_scan.rs_nkeys * sizeof(ScanKeyData));
/*
* Currently, we don't have a stats counter for bitmap heap scans (but the
@@ -304,7 +304,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* update stats for tuple fetches there)
*/
if (!scan->rs_bitmapscan && !scan->rs_samplescan)
- pgstat_count_heap_scan(scan->rs_rd);
+ pgstat_count_heap_scan(scan->rs_scan.rs_rd);
}
/*
@@ -314,16 +314,19 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
+heap_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
- Assert(!scan->rs_inited); /* else too late to change */
- Assert(!scan->rs_syncscan); /* else rs_startblock is significant */
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ Assert(!scan->rs_scan.rs_inited); /* else too late to change */
+ Assert(!scan->rs_pagescan.rs_syncscan); /* else rs_startblock is
+ * significant */
/* Check startBlk is valid (but allow case of zero blocks...) */
- Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
+ Assert(startBlk == 0 || startBlk < scan->rs_pagescan.rs_nblocks);
- scan->rs_startblock = startBlk;
- scan->rs_numblocks = numBlks;
+ scan->rs_pagescan.rs_startblock = startBlk;
+ scan->rs_pagescan.rs_numblocks = numBlks;
}
/*
@@ -334,8 +337,9 @@ heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk, BlockNumber numBlks)
* which tuples on the page are visible.
*/
void
-heapgetpage(HeapScanDesc scan, BlockNumber page)
+heapgetpage(StorageScanDesc sscan, BlockNumber page)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
Buffer buffer;
Snapshot snapshot;
Page dp;
@@ -345,13 +349,13 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
ItemId lpp;
bool all_visible;
- Assert(page < scan->rs_nblocks);
+ Assert(page < scan->rs_pagescan.rs_nblocks);
/* release previous scan buffer, if any */
- if (BufferIsValid(scan->rs_cbuf))
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
{
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
}
/*
@@ -362,20 +366,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
CHECK_FOR_INTERRUPTS();
/* read page using selected strategy */
- scan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM, page,
- RBM_NORMAL, scan->rs_strategy);
- scan->rs_cblock = page;
+ scan->rs_scan.rs_cbuf = ReadBufferExtended(scan->rs_scan.rs_rd, MAIN_FORKNUM, page,
+ RBM_NORMAL, scan->rs_pagescan.rs_strategy);
+ scan->rs_scan.rs_cblock = page;
- if (!scan->rs_pageatatime)
+ if (!scan->rs_pagescan.rs_pageatatime)
return;
- buffer = scan->rs_cbuf;
- snapshot = scan->rs_snapshot;
+ buffer = scan->rs_scan.rs_cbuf;
+ snapshot = scan->rs_scan.rs_snapshot;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
- heap_page_prune_opt(scan->rs_rd, buffer);
+ heap_page_prune_opt(scan->rs_scan.rs_rd, buffer);
/*
* We must hold share lock on the buffer content while examining tuple
@@ -385,7 +389,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = BufferGetPage(buffer);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
ntup = 0;
@@ -420,7 +424,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
HeapTupleData loctup;
bool valid;
- loctup.t_tableOid = RelationGetRelid(scan->rs_rd);
+ loctup.t_tableOid = RelationGetRelid(scan->rs_scan.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
@@ -428,20 +432,20 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, &loctup,
buffer, snapshot);
if (valid)
- scan->rs_vistuples[ntup++] = lineoff;
+ scan->rs_pagescan.rs_vistuples[ntup++] = lineoff;
}
}
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ scan->rs_pagescan.rs_ntuples = ntup;
}
/* ----------------
@@ -474,7 +478,7 @@ heapgettup(HeapScanDesc scan,
ScanKey key)
{
HeapTuple tuple = &(scan->rs_ctup);
- Snapshot snapshot = scan->rs_snapshot;
+ Snapshot snapshot = scan->rs_scan.rs_snapshot;
bool backward = ScanDirectionIsBackward(dir);
BlockNumber page;
bool finished;
@@ -489,14 +493,14 @@ heapgettup(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -509,29 +513,29 @@ heapgettup(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc) scan, page);
lineoff = FirstOffsetNumber; /* first offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
lineoff = /* next offnum */
OffsetNumberNext(ItemPointerGetOffsetNumber(&(tuple->t_self)));
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
/* page and lineoff now reference the physically next tid */
@@ -542,14 +546,14 @@ heapgettup(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -560,30 +564,30 @@ heapgettup(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc) scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber(dp);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineoff = lines; /* final offnum */
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
@@ -599,20 +603,20 @@ heapgettup(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -643,21 +647,21 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine,
tuple,
snapshot,
- scan->rs_cbuf);
+ scan->rs_scan.rs_cbuf);
- CheckForSerializableConflictOut(valid, scan->rs_rd, tuple,
- scan->rs_cbuf, snapshot);
+ CheckForSerializableConflictOut(valid, scan->rs_scan.rs_rd, tuple,
+ scan->rs_scan.rs_cbuf, snapshot);
if (valid && key != NULL)
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -682,17 +686,17 @@ heapgettup(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* advance to next/prior page and detect end of scan
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -703,10 +707,10 @@ heapgettup(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -720,8 +724,8 @@ heapgettup(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -729,21 +733,21 @@ heapgettup(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc) scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(snapshot, scan->rs_scan.rs_rd, dp);
lines = PageGetMaxOffsetNumber((Page) dp);
linesleft = lines;
if (backward)
@@ -794,14 +798,14 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (ScanDirectionIsForward(dir))
{
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -814,28 +818,28 @@ heapgettup_pagemode(HeapScanDesc scan,
/* Other processes might have already finished the scan. */
if (page == InvalidBlockNumber)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
}
else
- page = scan->rs_startblock; /* first page */
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_startblock; /* first page */
+ heapgetpage((StorageScanDesc) scan, page);
lineindex = 0;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
- lineindex = scan->rs_cindex + 1;
+ page = scan->rs_scan.rs_cblock; /* current page */
+ lineindex = scan->rs_pagescan.rs_cindex + 1;
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
/* page and lineindex now reference the next visible tid */
linesleft = lines - lineindex;
@@ -845,14 +849,14 @@ heapgettup_pagemode(HeapScanDesc scan,
/* backward parallel scan not supported */
Assert(scan->rs_parallel == NULL);
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+ if (scan->rs_pagescan.rs_nblocks == 0 || scan->rs_pagescan.rs_numblocks == 0)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
@@ -863,33 +867,33 @@ heapgettup_pagemode(HeapScanDesc scan,
* time, and much more likely that we'll just bollix things for
* forward scanners.
*/
- scan->rs_syncscan = false;
+ scan->rs_pagescan.rs_syncscan = false;
/* start from last page of the scan */
- if (scan->rs_startblock > 0)
- page = scan->rs_startblock - 1;
+ if (scan->rs_pagescan.rs_startblock > 0)
+ page = scan->rs_pagescan.rs_startblock - 1;
else
- page = scan->rs_nblocks - 1;
- heapgetpage(scan, page);
+ page = scan->rs_pagescan.rs_nblocks - 1;
+ heapgetpage((StorageScanDesc) scan, page);
}
else
{
/* continue from previously returned page/tuple */
- page = scan->rs_cblock; /* current page */
+ page = scan->rs_scan.rs_cblock; /* current page */
}
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
lineindex = lines - 1;
- scan->rs_inited = true;
+ scan->rs_scan.rs_inited = true;
}
else
{
- lineindex = scan->rs_cindex - 1;
+ lineindex = scan->rs_pagescan.rs_cindex - 1;
}
/* page and lineindex now reference the previous visible tid */
@@ -900,20 +904,20 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* ``no movement'' scan direction: refetch prior tuple
*/
- if (!scan->rs_inited)
+ if (!scan->rs_scan.rs_inited)
{
- Assert(!BufferIsValid(scan->rs_cbuf));
+ Assert(!BufferIsValid(scan->rs_scan.rs_cbuf));
tuple->t_data = NULL;
return;
}
page = ItemPointerGetBlockNumber(&(tuple->t_self));
- if (page != scan->rs_cblock)
- heapgetpage(scan, page);
+ if (page != scan->rs_scan.rs_cblock)
+ heapgetpage((StorageScanDesc) scan, page);
/* Since the tuple was previously fetched, needn't lock page here */
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
lineoff = ItemPointerGetOffsetNumber(&(tuple->t_self));
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -922,8 +926,8 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_len = ItemIdGetLength(lpp);
/* check that rs_cindex is in sync */
- Assert(scan->rs_cindex < scan->rs_ntuples);
- Assert(lineoff == scan->rs_vistuples[scan->rs_cindex]);
+ Assert(scan->rs_pagescan.rs_cindex < scan->rs_pagescan.rs_ntuples);
+ Assert(lineoff == scan->rs_pagescan.rs_vistuples[scan->rs_pagescan.rs_cindex]);
return;
}
@@ -936,7 +940,7 @@ heapgettup_pagemode(HeapScanDesc scan,
{
while (linesleft > 0)
{
- lineoff = scan->rs_vistuples[lineindex];
+ lineoff = scan->rs_pagescan.rs_vistuples[lineindex];
lpp = PageGetItemId(dp, lineoff);
Assert(ItemIdIsNormal(lpp));
@@ -947,7 +951,7 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(scan->rs_scan.rs_rd->rd_stamroutine, tuple, scan->rs_scan.rs_snapshot, scan->rs_scan.rs_cbuf))
{
/*
* if current tuple qualifies, return it.
@@ -956,19 +960,19 @@ heapgettup_pagemode(HeapScanDesc scan,
{
bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_scan.rs_rd),
nkeys, key, valid);
if (valid)
{
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ scan->rs_pagescan.rs_cindex = lineindex;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
else
{
- scan->rs_cindex = lineindex;
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ scan->rs_pagescan.rs_cindex = lineindex;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
@@ -987,7 +991,7 @@ heapgettup_pagemode(HeapScanDesc scan,
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
*/
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_UNLOCK);
/*
* if we get here, it means we've exhausted the items on this page and
@@ -995,10 +999,10 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (backward)
{
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
if (page == 0)
- page = scan->rs_nblocks;
+ page = scan->rs_pagescan.rs_nblocks;
page--;
}
else if (scan->rs_parallel != NULL)
@@ -1009,10 +1013,10 @@ heapgettup_pagemode(HeapScanDesc scan,
else
{
page++;
- if (page >= scan->rs_nblocks)
+ if (page >= scan->rs_pagescan.rs_nblocks)
page = 0;
- finished = (page == scan->rs_startblock) ||
- (scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
+ finished = (page == scan->rs_pagescan.rs_startblock) ||
+ (scan->rs_pagescan.rs_numblocks != InvalidBlockNumber ? --scan->rs_pagescan.rs_numblocks == 0 : false);
/*
* Report our new scan position for synchronization purposes. We
@@ -1026,8 +1030,8 @@ heapgettup_pagemode(HeapScanDesc scan,
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
- ss_report_location(scan->rs_rd, page);
+ if (scan->rs_pagescan.rs_syncscan)
+ ss_report_location(scan->rs_scan.rs_rd, page);
}
/*
@@ -1035,21 +1039,21 @@ heapgettup_pagemode(HeapScanDesc scan,
*/
if (finished)
{
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
- scan->rs_cbuf = InvalidBuffer;
- scan->rs_cblock = InvalidBlockNumber;
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
+ scan->rs_scan.rs_cbuf = InvalidBuffer;
+ scan->rs_scan.rs_cblock = InvalidBlockNumber;
tuple->t_data = NULL;
- scan->rs_inited = false;
+ scan->rs_scan.rs_inited = false;
return;
}
- heapgetpage(scan, page);
+ heapgetpage((StorageScanDesc) scan, page);
- LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- dp = BufferGetPage(scan->rs_cbuf);
- TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
- lines = scan->rs_ntuples;
+ LockBuffer(scan->rs_scan.rs_cbuf, BUFFER_LOCK_SHARE);
+ dp = BufferGetPage(scan->rs_scan.rs_cbuf);
+ TestForOldSnapshot(scan->rs_scan.rs_snapshot, scan->rs_scan.rs_rd, dp);
+ lines = scan->rs_pagescan.rs_ntuples;
linesleft = lines;
if (backward)
lineindex = lines - 1;
@@ -1376,7 +1380,7 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
return r;
}
-HeapScanDesc
+StorageScanDesc
heap_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
ParallelHeapScanDesc parallel_scan,
@@ -1403,12 +1407,12 @@ heap_beginscan(Relation relation, Snapshot snapshot,
*/
scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
- scan->rs_rd = relation;
- scan->rs_snapshot = snapshot;
- scan->rs_nkeys = nkeys;
+ scan->rs_scan.rs_rd = relation;
+ scan->rs_scan.rs_snapshot = snapshot;
+ scan->rs_scan.rs_nkeys = nkeys;
scan->rs_bitmapscan = is_bitmapscan;
scan->rs_samplescan = is_samplescan;
- scan->rs_strategy = NULL; /* set in initscan */
+ scan->rs_pagescan.rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_temp_snap = temp_snap;
@@ -1417,7 +1421,7 @@ heap_beginscan(Relation relation, Snapshot snapshot,
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
*/
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(snapshot);
/*
* For a seqscan in a serializable transaction, acquire a predicate lock
@@ -1441,13 +1445,13 @@ heap_beginscan(Relation relation, Snapshot snapshot,
* initscan() and we don't want to allocate memory again
*/
if (nkeys > 0)
- scan->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+ scan->rs_scan.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else
- scan->rs_key = NULL;
+ scan->rs_scan.rs_key = NULL;
initscan(scan, key, false);
- return scan;
+ return (StorageScanDesc) scan;
}
/* ----------------
@@ -1455,21 +1459,23 @@ heap_beginscan(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-heap_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+heap_rescan(StorageScanDesc sscan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
if (set_params)
{
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ scan->rs_pagescan.rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_scan.rs_snapshot);
}
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* reinitialize scan descriptor
@@ -1500,29 +1506,31 @@ heap_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
* ----------------
*/
void
-heap_endscan(HeapScanDesc scan)
+heap_endscan(StorageScanDesc sscan)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
/*
* unpin scan buffers
*/
- if (BufferIsValid(scan->rs_cbuf))
- ReleaseBuffer(scan->rs_cbuf);
+ if (BufferIsValid(scan->rs_scan.rs_cbuf))
+ ReleaseBuffer(scan->rs_scan.rs_cbuf);
/*
* decrement relation reference count and free scan descriptor storage
*/
- RelationDecrementReferenceCount(scan->rs_rd);
+ RelationDecrementReferenceCount(scan->rs_scan.rs_rd);
- if (scan->rs_key)
- pfree(scan->rs_key);
+ if (scan->rs_scan.rs_key)
+ pfree(scan->rs_scan.rs_key);
- if (scan->rs_strategy != NULL)
- FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_pagescan.rs_strategy != NULL)
+ FreeAccessStrategy(scan->rs_pagescan.rs_strategy);
if (scan->rs_temp_snap)
- UnregisterSnapshot(scan->rs_snapshot);
+ UnregisterSnapshot(scan->rs_scan.rs_snapshot);
pfree(scan);
}
@@ -1618,7 +1626,7 @@ retry:
else
{
SpinLockRelease(¶llel_scan->phs_mutex);
- sync_startpage = ss_get_location(scan->rs_rd, scan->rs_nblocks);
+ sync_startpage = ss_get_location(scan->rs_scan.rs_rd, scan->rs_pagescan.rs_nblocks);
goto retry;
}
}
@@ -1660,10 +1668,10 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* starting block number, modulo nblocks.
*/
nallocated = pg_atomic_fetch_add_u64(¶llel_scan->phs_nallocated, 1);
- if (nallocated >= scan->rs_nblocks)
+ if (nallocated >= scan->rs_pagescan.rs_nblocks)
page = InvalidBlockNumber; /* all blocks have been allocated */
else
- page = (nallocated + parallel_scan->phs_startblock) % scan->rs_nblocks;
+ page = (nallocated + parallel_scan->phs_startblock) % scan->rs_pagescan.rs_nblocks;
/*
* Report scan location. Normally, we report the current page number.
@@ -1672,12 +1680,12 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* doesn't slew backwards. We only report the position at the end of the
* scan once, though: subsequent callers will report nothing.
*/
- if (scan->rs_syncscan)
+ if (scan->rs_pagescan.rs_syncscan)
{
if (page != InvalidBlockNumber)
- ss_report_location(scan->rs_rd, page);
- else if (nallocated == scan->rs_nblocks)
- ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
+ ss_report_location(scan->rs_scan.rs_rd, page);
+ else if (nallocated == scan->rs_pagescan.rs_nblocks)
+ ss_report_location(scan->rs_scan.rs_rd, parallel_scan->phs_startblock);
}
return page;
@@ -1690,12 +1698,14 @@ heap_parallelscan_nextpage(HeapScanDesc scan)
* ----------------
*/
void
-heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+heap_update_snapshot(StorageScanDesc sscan, Snapshot snapshot)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
Assert(IsMVCCSnapshot(snapshot));
RegisterSnapshot(snapshot);
- scan->rs_snapshot = snapshot;
+ scan->rs_scan.rs_snapshot = snapshot;
scan->rs_temp_snap = true;
}
@@ -1723,17 +1733,19 @@ heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
#endif /* !defined(HEAPDEBUGALL) */
StorageTuple
-heap_getnext(HeapScanDesc scan, ScanDirection direction)
+heap_getnext(StorageScanDesc sscan, ScanDirection direction)
{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
/* Note: no locking manipulations needed */
HEAPDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -1747,7 +1759,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
*/
HEAPDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return heap_copytuple(&(scan->rs_ctup));
}
@@ -1755,7 +1767,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
#ifdef HEAPAMSLOTDEBUGALL
#define HEAPAMSLOTDEBUG_1 \
elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
- RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+ RelationGetRelationName(scan->rs_scan.rs_rd), scan->rs_scan.rs_nkeys, (int) direction)
#define HEAPAMSLOTDEBUG_2 \
elog(DEBUG2, "heapam_getnext returning EOS")
#define HEAPAMSLOTDEBUG_3 \
@@ -1767,7 +1779,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
#endif
TupleTableSlot *
-heap_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+heap_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
HeapScanDesc scan = (HeapScanDesc) sscan;
@@ -1775,11 +1787,11 @@ heap_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *sl
HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
- if (scan->rs_pageatatime)
+ if (scan->rs_pagescan.rs_pageatatime)
heapgettup_pagemode(scan, direction,
- scan->rs_nkeys, scan->rs_key);
+ scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
else
- heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+ heapgettup(scan, direction, scan->rs_scan.rs_nkeys, scan->rs_scan.rs_key);
if (scan->rs_ctup.t_data == NULL)
{
@@ -1794,7 +1806,7 @@ heap_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *sl
*/
HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
- pgstat_count_heap_getnext(scan->rs_rd);
+ pgstat_count_heap_getnext(scan->rs_scan.rs_rd);
return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
slot, InvalidBuffer, true);
}
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 4ee3def639..bccc1f8700 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -20,7 +20,10 @@
*/
#include "postgres.h"
+#include "access/heapam.h"
+#include "access/relscan.h"
#include "access/storageamapi.h"
+#include "pgstat.h"
#include "utils/builtins.h"
#include "utils/rel.h"
@@ -241,6 +244,44 @@ heapam_form_tuple_by_datum(Datum data, Oid tableoid)
return heap_form_tuple_by_datum(data, tableoid);
}
+static ParallelHeapScanDesc
+heapam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return scan->rs_parallel;
+}
+
+static HeapPageScanDesc
+heapam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ return &scan->rs_pagescan;
+}
+
+static StorageTuple
+heapam_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+ Page dp;
+ ItemId lp;
+
+ dp = (Page) BufferGetPage(scan->rs_scan.rs_cbuf);
+ lp = PageGetItemId(dp, offset);
+ Assert(ItemIdIsNormal(lp));
+
+ scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+ scan->rs_ctup.t_len = ItemIdGetLength(lp);
+ scan->rs_ctup.t_tableOid = scan->rs_scan.rs_rd->rd_id;
+ ItemPointerSet(&scan->rs_ctup.t_self, blkno, offset);
+
+ pgstat_count_heap_fetch(scan->rs_scan.rs_rd);
+
+ return &(scan->rs_ctup);
+}
+
+
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
@@ -261,6 +302,19 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->scan_rescan = heap_rescan;
amroutine->scan_update_snapshot = heap_update_snapshot;
amroutine->hot_search_buffer = heap_hot_search_buffer;
+ amroutine->scan_fetch_tuple_from_offset = heapam_fetch_tuple_from_offset;
+
+ /*
+ * The following routine needs to be provided when the storage support
+ * parallel sequential scan
+ */
+ amroutine->scan_get_parallelheapscandesc = heapam_get_parallelheapscandesc;
+
+ /*
+ * The following routine needs to be provided when the storage support
+ * BitmapHeap and Sample Scans
+ */
+ amroutine->scan_get_heappagescandesc = heapam_get_heappagescandesc;
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 26a9ccb657..ee9352df06 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -478,10 +478,10 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
}
else
{
- HeapScanDesc scan = sysscan->scan;
+ StorageScanDesc scan = sysscan->scan;
Assert(IsMVCCSnapshot(scan->rs_snapshot));
- Assert(tup == &scan->rs_ctup);
+ /* hari Assert(tup == &scan->rs_ctup); */
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c
index c326a54f70..dfd28b55cd 100644
--- a/src/backend/access/storage/storageam.c
+++ b/src/backend/access/storage/storageam.c
@@ -56,7 +56,7 @@ storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
* Caller must hold a suitable lock on the correct relation.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
{
Snapshot snapshot;
@@ -69,6 +69,25 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
true, true, true, false, false, true);
}
+ParallelHeapScanDesc
+storageam_get_parallelheapscandesc(StorageScanDesc sscan)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_get_parallelheapscandesc(sscan);
+}
+
+HeapPageScanDesc
+storageam_get_heappagescandesc(StorageScanDesc sscan)
+{
+ /*
+ * Planner should have already validated whether the current storage
+ * supports Page scans are not? This function will be called only from
+ * Bitmap Heap scan and sample scan
+ */
+ Assert(sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc != NULL);
+
+ return sscan->rs_rd->rd_stamroutine->scan_get_heappagescandesc(sscan);
+}
+
/*
* heap_setscanlimits - restrict range of a heapscan
*
@@ -76,7 +95,7 @@ storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan
* numBlks is number of pages to scan (InvalidBlockNumber means "all")
*/
void
-storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
{
sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
}
@@ -106,7 +125,7 @@ storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numB
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
-HeapScanDesc
+StorageScanDesc
storage_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -114,7 +133,7 @@ storage_beginscan(Relation relation, Snapshot snapshot,
true, true, true, false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
{
Oid relid = RelationGetRelid(relation);
@@ -124,7 +143,7 @@ storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
true, true, true, false, false, true);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync)
@@ -134,7 +153,7 @@ storage_beginscan_strat(Relation relation, Snapshot snapshot,
false, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_bm(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key)
{
@@ -142,7 +161,7 @@ storage_beginscan_bm(Relation relation, Snapshot snapshot,
false, false, true, true, false, false);
}
-HeapScanDesc
+StorageScanDesc
storage_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -157,7 +176,7 @@ storage_beginscan_sampling(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-storage_rescan(HeapScanDesc scan,
+storage_rescan(StorageScanDesc scan,
ScanKey key)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
@@ -173,7 +192,7 @@ storage_rescan(HeapScanDesc scan,
* ----------------
*/
void
-storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
@@ -188,7 +207,7 @@ storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
* ----------------
*/
void
-storage_endscan(HeapScanDesc scan)
+storage_endscan(StorageScanDesc scan)
{
scan->rs_rd->rd_stamroutine->scan_end(scan);
}
@@ -201,23 +220,29 @@ storage_endscan(HeapScanDesc scan)
* ----------------
*/
void
-storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot)
{
scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
}
StorageTuple
-storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+storage_getnext(StorageScanDesc sscan, ScanDirection direction)
{
return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
}
TupleTableSlot *
-storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
{
return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
}
+StorageTuple
+storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_fetch_tuple_from_offset(sscan, blkno, offset);
+}
+
/*
* Insert a tuple from a slot into storage AM routine
*/
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index e270cbc4a0..8793b95c08 100644
--- a/src/backend/access/tablesample/system.c
+++ b/src/backend/access/tablesample/system.c
@@ -183,7 +183,7 @@ static BlockNumber
system_nextsampleblock(SampleScanState *node)
{
SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc scan = node->pagescan;
BlockNumber nextblock = sampler->nextblock;
uint32 hashinput[2];
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index a73a363a49..da3d48b9cc 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -577,7 +577,7 @@ boot_openrel(char *relname)
int i;
struct typmap **app;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
if (strlen(relname) >= NAMEDATALEN)
@@ -893,7 +893,7 @@ gettype(char *type)
{
int i;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
struct typmap **app;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 0b3be57a88..c058a1bccd 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -822,7 +822,7 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
ScanKeyData key[2];
int keycount;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
keycount = 0;
@@ -880,7 +880,7 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ScanKeyInit(&key[0],
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 16bb70ef45..1c508767cf 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1900,7 +1900,7 @@ index_update_stats(Relation rel,
ReindexIsProcessingHeap(RelationRelationId))
{
/* don't assume syscache will work */
- HeapScanDesc pg_class_scan;
+ StorageScanDesc pg_class_scan;
ScanKeyData key[1];
ScanKeyInit(&key[0],
@@ -2213,7 +2213,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
{
bool is_system_catalog;
bool checking_uniqueness;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2652,7 +2652,7 @@ IndexCheckExclusion(Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
@@ -2966,7 +2966,7 @@ validate_index_heapscan(Relation heapRelation,
Snapshot snapshot,
v_i_state *state)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index d6f1a9cad5..708f8f39e8 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1265,7 +1265,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
Snapshot snapshot;
TupleDesc tupdesc;
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
MemoryContext oldCxt;
TupleTableSlot *tupslot;
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 1d048e6394..842abcc8b5 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -151,7 +151,7 @@ RemoveConversionById(Oid conversionOid)
{
Relation rel;
HeapTuple tuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scanKeyData;
ScanKeyInit(&scanKeyData,
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 517e3101cd..63324cfc8e 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -171,7 +171,7 @@ void
DropSetting(Oid databaseid, Oid roleid)
{
Relation relsetting;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData keys[2];
HeapTuple tup;
int numkeys = 0;
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 145e3c1d65..5e3915b438 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -314,7 +314,7 @@ GetAllTablesPublicationRelations(void)
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index a51f2e4dfc..050dfa3e4c 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -377,7 +377,7 @@ void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData skey[2];
HeapTuple tup;
int nkeys = 0;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e0f6973a3f..ccdbe70ff6 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -746,7 +746,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
Datum *values;
bool *isnull;
IndexScanDesc indexScan;
- HeapScanDesc heapScan;
+ StorageScanDesc heapScan;
bool use_wal;
bool is_system_catalog;
TransactionId OldestXmin;
@@ -1638,7 +1638,7 @@ static List *
get_tables_to_cluster(MemoryContext cluster_context)
{
Relation indRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData entry;
HeapTuple indexTuple;
Form_pg_index index;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f2209d9f81..42803c687b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2032,7 +2032,7 @@ CopyTo(CopyState cstate)
{
Datum *values;
bool *nulls;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 87d651002e..4a224b5bbc 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -99,7 +99,7 @@ static int errdetail_busy_db(int notherbackends, int npreparedxacts);
Oid
createdb(ParseState *pstate, const CreatedbStmt *stmt)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
Relation rel;
Oid src_dboid;
Oid src_owner;
@@ -1872,7 +1872,7 @@ static void
remove_dbtablespaces(Oid db_id)
{
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
@@ -1939,7 +1939,7 @@ check_db_file_conflict(Oid db_id)
{
bool result = false;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 400f936a2f..2152aa86e5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1891,7 +1891,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{
Oid objectOid;
Relation relationRelation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
ScanKeyData scan_keys[1];
HeapTuple tuple;
MemoryContext private_context;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3d4fa831b3..28874a27ae 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4492,7 +4492,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
MemoryContext oldCxt;
List *dropped_attrs = NIL;
@@ -5065,7 +5065,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
{
Relation classRel;
ScanKeyData key[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
List *result = NIL;
@@ -8219,7 +8219,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Expr *origexpr;
ExprState *exprstate;
TupleDesc tupdesc;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
ExprContext *econtext;
MemoryContext oldcxt;
@@ -8302,7 +8302,7 @@ validateForeignKeyConstraint(char *conname,
Oid pkindOid,
Oid constraintOid)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Trigger trig;
Snapshot snapshot;
@@ -10809,7 +10809,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ListCell *l;
ScanKeyData key[1];
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 6804182db7..0e2b93a5df 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -402,7 +402,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
{
#ifdef HAVE_SYMLINK
char *tablespacename = stmt->tablespacename;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
Relation rel;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -913,7 +913,7 @@ RenameTableSpace(const char *oldname, const char *newname)
Oid tspId;
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
HeapTuple newtuple;
Form_pg_tablespace newform;
@@ -988,7 +988,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
{
Relation rel;
ScanKeyData entry[1];
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tup;
Oid tablespaceoid;
Datum datum;
@@ -1382,7 +1382,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
{
Oid result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
@@ -1428,7 +1428,7 @@ get_tablespace_name(Oid spc_oid)
{
char *result;
Relation rel;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
HeapTuple tuple;
ScanKeyData entry[1];
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8a339d16be..e539570d9c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2382,7 +2382,7 @@ AlterDomainNotNull(List *names, bool notNull)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
@@ -2778,7 +2778,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
RelToCheck *rtc = (RelToCheck *) lfirst(rt);
Relation testrel = rtc->rel;
TupleDesc tupdesc = RelationGetDescr(testrel);
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Snapshot snapshot;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 3a8dc8eb0c..42e462861b 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -529,7 +529,7 @@ get_all_vacuum_rels(void)
{
List *vacrels = NIL;
Relation pgclass;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
@@ -1183,7 +1183,7 @@ vac_truncate_clog(TransactionId frozenXID,
{
TransactionId nextXID = ReadNewTransactionId();
Relation relation;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tuple;
Oid oldestxid_datoid;
Oid minmulti_datoid;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 0c5ae8fa41..fc6c4f909a 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -55,14 +55,14 @@
static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
-static void bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres);
+static void bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres);
static inline void BitmapDoneInitializingSharedState(
ParallelBitmapHeapState *pstate);
static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node,
TBMIterateResult *tbmres);
static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
static inline void BitmapPrefetch(BitmapHeapScanState *node,
- HeapScanDesc scan);
+ StorageScanDesc scan);
static bool BitmapShouldInitializeSharedState(
ParallelBitmapHeapState *pstate);
@@ -77,7 +77,8 @@ static TupleTableSlot *
BitmapHeapNext(BitmapHeapScanState *node)
{
ExprContext *econtext;
- HeapScanDesc scan;
+ StorageScanDesc scan;
+ HeapPageScanDesc pagescan;
TIDBitmap *tbm;
TBMIterator *tbmiterator = NULL;
TBMSharedIterator *shared_tbmiterator = NULL;
@@ -93,6 +94,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext = node->ss.ps.ps_ExprContext;
slot = node->ss.ss_ScanTupleSlot;
scan = node->ss.ss_currentScanDesc;
+ pagescan = node->pagescan;
tbm = node->tbm;
if (pstate == NULL)
tbmiterator = node->tbmiterator;
@@ -192,8 +194,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
for (;;)
{
- Page dp;
- ItemId lp;
+ StorageTuple tuple;
CHECK_FOR_INTERRUPTS();
@@ -220,7 +221,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
* least AccessShareLock on the table before performing any of the
* indexscans, but let's be safe.)
*/
- if (tbmres->blockno >= scan->rs_nblocks)
+ if (tbmres->blockno >= pagescan->rs_nblocks)
{
node->tbmres = tbmres = NULL;
continue;
@@ -243,14 +244,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
* The number of tuples on this page is put into
* scan->rs_ntuples; note we don't fill scan->rs_vistuples.
*/
- scan->rs_ntuples = tbmres->ntuples;
+ pagescan->rs_ntuples = tbmres->ntuples;
}
else
{
/*
* Fetch the current heap page and identify candidate tuples.
*/
- bitgetpage(scan, tbmres);
+ bitgetpage(node, tbmres);
}
if (tbmres->ntuples >= 0)
@@ -261,7 +262,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Set rs_cindex to first slot to examine
*/
- scan->rs_cindex = 0;
+ pagescan->rs_cindex = 0;
/* Adjust the prefetch target */
BitmapAdjustPrefetchTarget(node);
@@ -271,7 +272,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Continuing in previously obtained page; advance rs_cindex
*/
- scan->rs_cindex++;
+ pagescan->rs_cindex++;
#ifdef USE_PREFETCH
@@ -298,7 +299,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Out of range? If so, nothing more to look at on this page
*/
- if (scan->rs_cindex < 0 || scan->rs_cindex >= scan->rs_ntuples)
+ if (pagescan->rs_cindex < 0 || pagescan->rs_cindex >= pagescan->rs_ntuples)
{
node->tbmres = tbmres = NULL;
continue;
@@ -325,23 +326,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
/*
* Okay to fetch the tuple.
*/
- targoffset = scan->rs_vistuples[scan->rs_cindex];
- dp = (Page) BufferGetPage(scan->rs_cbuf);
- lp = PageGetItemId(dp, targoffset);
- Assert(ItemIdIsNormal(lp));
-
- scan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- scan->rs_ctup.t_len = ItemIdGetLength(lp);
- scan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&scan->rs_ctup.t_self, tbmres->blockno, targoffset);
-
- pgstat_count_heap_fetch(scan->rs_rd);
+ targoffset = pagescan->rs_vistuples[pagescan->rs_cindex];
+ tuple = storage_fetch_tuple_from_offset(scan, tbmres->blockno, targoffset);
/*
* Set up the result slot to point to this tuple. Note that the
* slot acquires a pin on the buffer.
*/
- ExecStoreTuple(&scan->rs_ctup,
+ ExecStoreTuple(tuple,
slot,
scan->rs_cbuf,
false);
@@ -383,8 +375,10 @@ BitmapHeapNext(BitmapHeapScanState *node)
* interesting according to the bitmap, and visible according to the snapshot.
*/
static void
-bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
+bitgetpage(BitmapHeapScanState *node, TBMIterateResult *tbmres)
{
+ StorageScanDesc scan = node->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = node->pagescan;
BlockNumber page = tbmres->blockno;
Buffer buffer;
Snapshot snapshot;
@@ -393,7 +387,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
/*
* Acquire pin on the target heap page, trading in any pin we held before.
*/
- Assert(page < scan->rs_nblocks);
+ Assert(page < pagescan->rs_nblocks);
scan->rs_cbuf = ReleaseAndReadBuffer(scan->rs_cbuf,
scan->rs_rd,
@@ -436,7 +430,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
ItemPointerSet(&tid, page, offnum);
if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
&heapTuple, NULL, true))
- scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
+ pagescan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
@@ -452,23 +446,20 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
{
ItemId lp;
- HeapTupleData loctup;
+ StorageTuple loctup;
bool valid;
lp = PageGetItemId(dp, offnum);
if (!ItemIdIsNormal(lp))
continue;
- loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
- loctup.t_len = ItemIdGetLength(lp);
- loctup.t_tableOid = scan->rs_rd->rd_id;
- ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
+ loctup = storage_fetch_tuple_from_offset(scan, page, offnum);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, loctup, snapshot, buffer);
if (valid)
{
- scan->rs_vistuples[ntup++] = offnum;
- PredicateLockTuple(scan->rs_rd, &loctup, snapshot);
+ pagescan->rs_vistuples[ntup++] = offnum;
+ PredicateLockTuple(scan->rs_rd, loctup, snapshot);
}
- CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+ CheckForSerializableConflictOut(valid, scan->rs_rd, loctup,
buffer, snapshot);
}
}
@@ -476,7 +467,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
Assert(ntup <= MaxHeapTuplesPerPage);
- scan->rs_ntuples = ntup;
+ pagescan->rs_ntuples = ntup;
}
/*
@@ -602,7 +593,7 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
* BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
*/
static inline void
-BitmapPrefetch(BitmapHeapScanState *node, HeapScanDesc scan)
+BitmapPrefetch(BitmapHeapScanState *node, StorageScanDesc scan)
{
#ifdef USE_PREFETCH
ParallelBitmapHeapState *pstate = node->pstate;
@@ -793,7 +784,7 @@ void
ExecEndBitmapHeapScan(BitmapHeapScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* extract information from the node
@@ -958,6 +949,8 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
0,
NULL);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
+
/*
* get the scan type from the relation descriptor.
*/
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 7e990dc35e..064b655aea 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -31,9 +31,7 @@ static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
static StorageTuple tablesample_getnext(SampleScanState *scanstate);
static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
- HeapScanDesc scan);
-
-/* hari */
+ SampleScanState *scanstate);
/* ----------------------------------------------------------------
* Scan Support
@@ -357,6 +355,7 @@ tablesample_init(SampleScanState *scanstate)
scanstate->use_bulkread,
allow_sync,
scanstate->use_pagemode);
+ scanstate->pagescan = storageam_get_heappagescandesc(scanstate->ss.ss_currentScanDesc);
}
else
{
@@ -382,10 +381,11 @@ static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
- HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
- HeapTuple tuple = &(scan->rs_ctup);
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+ StorageTuple tuple;
Snapshot snapshot = scan->rs_snapshot;
- bool pagemode = scan->rs_pageatatime;
+ bool pagemode = pagescan->rs_pageatatime;
BlockNumber blockno;
Page page;
bool all_visible;
@@ -396,10 +396,9 @@ tablesample_getnext(SampleScanState *scanstate)
/*
* return null immediately if relation is empty
*/
- if (scan->rs_nblocks == 0)
+ if (pagescan->rs_nblocks == 0)
{
Assert(!BufferIsValid(scan->rs_cbuf));
- tuple->t_data = NULL;
return NULL;
}
if (tsm->NextSampleBlock)
@@ -407,13 +406,12 @@ tablesample_getnext(SampleScanState *scanstate)
blockno = tsm->NextSampleBlock(scanstate);
if (!BlockNumberIsValid(blockno))
{
- tuple->t_data = NULL;
return NULL;
}
}
else
- blockno = scan->rs_startblock;
- Assert(blockno < scan->rs_nblocks);
+ blockno = pagescan->rs_startblock;
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
scan->rs_inited = true;
}
@@ -456,14 +454,12 @@ tablesample_getnext(SampleScanState *scanstate)
if (!ItemIdIsNormal(itemid))
continue;
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
- tuple->t_len = ItemIdGetLength(itemid);
- ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
+ tuple = storage_fetch_tuple_from_offset(scan, blockno, tupoffset);
if (all_visible)
visible = true;
else
- visible = SampleTupleVisible(tuple, tupoffset, scan);
+ visible = SampleTupleVisible(tuple, tupoffset, scanstate);
/* in pagemode, heapgetpage did this for us */
if (!pagemode)
@@ -494,14 +490,14 @@ tablesample_getnext(SampleScanState *scanstate)
if (tsm->NextSampleBlock)
{
blockno = tsm->NextSampleBlock(scanstate);
- Assert(!scan->rs_syncscan);
+ Assert(!pagescan->rs_syncscan);
finished = !BlockNumberIsValid(blockno);
}
else
{
/* Without NextSampleBlock, just do a plain forward seqscan. */
blockno++;
- if (blockno >= scan->rs_nblocks)
+ if (blockno >= pagescan->rs_nblocks)
blockno = 0;
/*
@@ -514,10 +510,10 @@ tablesample_getnext(SampleScanState *scanstate)
* a little bit backwards on every invocation, which is confusing.
* We don't guarantee any specific ordering in general, though.
*/
- if (scan->rs_syncscan)
+ if (pagescan->rs_syncscan)
ss_report_location(scan->rs_rd, blockno);
- finished = (blockno == scan->rs_startblock);
+ finished = (blockno == pagescan->rs_startblock);
}
/*
@@ -529,12 +525,11 @@ tablesample_getnext(SampleScanState *scanstate)
ReleaseBuffer(scan->rs_cbuf);
scan->rs_cbuf = InvalidBuffer;
scan->rs_cblock = InvalidBlockNumber;
- tuple->t_data = NULL;
scan->rs_inited = false;
return NULL;
}
- Assert(blockno < scan->rs_nblocks);
+ Assert(blockno < pagescan->rs_nblocks);
heapgetpage(scan, blockno);
/* Re-establish state for new page */
@@ -549,16 +544,19 @@ tablesample_getnext(SampleScanState *scanstate)
/* Count successfully-fetched tuples as heap fetches */
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return tuple;
}
/*
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, SampleScanState *scanstate)
{
- if (scan->rs_pageatatime)
+ StorageScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ HeapPageScanDesc pagescan = scanstate->pagescan;
+
+ if (pagescan->rs_pageatatime)
{
/*
* In pageatatime mode, heapgetpage() already did visibility checks,
@@ -570,12 +568,12 @@ SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan
* gain to justify the restriction.
*/
int start = 0,
- end = scan->rs_ntuples - 1;
+ end = pagescan->rs_ntuples - 1;
while (start <= end)
{
int mid = (start + end) / 2;
- OffsetNumber curoffset = scan->rs_vistuples[mid];
+ OffsetNumber curoffset = pagescan->rs_vistuples[mid];
if (tupoffset == curoffset)
return true;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 7cfc2107f6..32484ea84c 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -317,9 +317,10 @@ void
ExecSeqScanReInitializeDSM(SeqScanState *node,
ParallelContext *pcxt)
{
- HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ ParallelHeapScanDesc pscan;
- heap_parallelscan_reinitialize(scan->rs_parallel);
+ pscan = storageam_get_parallelheapscandesc(node->ss.ss_currentScanDesc);
+ heap_parallelscan_reinitialize(pscan);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index f7438714c4..1d8caf3c79 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -21,6 +21,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xlog.h"
@@ -253,7 +254,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amsearchnulls = amroutine->amsearchnulls;
info->amcanparallel = amroutine->amcanparallel;
info->amhasgettuple = (amroutine->amgettuple != NULL);
- info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amhasgetbitmap = ((amroutine->amgetbitmap != NULL)
+ && (relation->rd_stamroutine->scan_get_heappagescandesc != NULL));
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0d2e1733bf..ee005d4168 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1865,7 +1865,7 @@ get_database_list(void)
{
List *dblist = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
@@ -1931,7 +1931,7 @@ do_autovacuum(void)
{
Relation classRel;
HeapTuple tuple;
- HeapScanDesc relScan;
+ StorageScanDesc relScan;
Form_pg_database dbForm;
List *table_oids = NIL;
List *orphan_oids = NIL;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 2b56b740a2..84f8411b97 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1207,7 +1207,7 @@ pgstat_collect_oids(Oid catalogid)
HTAB *htab;
HASHCTL hash_ctl;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
Snapshot snapshot;
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 83ec2dfcbe..73bb1770f1 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -107,7 +107,7 @@ get_subscription_list(void)
{
List *res = NIL;
Relation rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
HeapTuple tup;
MemoryContext resultcxt;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index c6791c758c..4140ff7603 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -420,7 +420,7 @@ DefineQueryRewrite(const char *rulename,
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
Snapshot snapshot;
if (event_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 03b7cc76d7..42a35824e5 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1208,7 +1208,7 @@ static bool
ThereIsAtLeastOneRole(void)
{
Relation pg_authid_rel;
- HeapScanDesc scan;
+ StorageScanDesc scan;
bool result;
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index be62547195..400b11dfd4 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -98,6 +98,8 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
#define heap_close(r,l) relation_close(r,l)
/* struct definitions appear in relscan.h */
+typedef struct HeapPageScanDescData *HeapPageScanDesc;
+typedef struct StorageScanDescData *StorageScanDesc;
typedef struct HeapScanDescData *HeapScanDesc;
typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
@@ -107,25 +109,25 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
*/
#define HeapScanIsValid(scan) PointerIsValid(scan)
-extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
+extern StorageScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+extern void heap_setscanlimits(StorageScanDesc scan, BlockNumber startBlk,
BlockNumber endBlk);
-extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+extern void heapgetpage(StorageScanDesc scan, BlockNumber page);
+extern void heap_rescan(StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern void heap_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void heap_endscan(HeapScanDesc scan);
-extern StorageTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
-extern TupleTableSlot *heap_getnextslot(HeapScanDesc sscan, ScanDirection direction,
+extern void heap_endscan(StorageScanDesc scan);
+extern StorageTuple heap_getnext(StorageScanDesc scan, ScanDirection direction);
+extern TupleTableSlot *heap_getnextslot(StorageScanDesc sscan, ScanDirection direction,
TupleTableSlot *slot);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
@@ -180,7 +182,7 @@ extern void simple_heap_update(Relation relation, ItemPointer otid,
HeapTuple tup);
extern void heap_sync(Relation relation);
-extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void heap_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
/* in heap/heapam_visibility.c */
extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index 147f862a2b..2651fd73d7 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/htup_details.h"
#include "access/itup.h"
#include "access/tupdesc.h"
@@ -42,40 +43,54 @@ typedef struct ParallelHeapScanDescData
char phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelHeapScanDescData;
-typedef struct HeapScanDescData
+typedef struct StorageScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
Snapshot rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
ScanKey rs_key; /* array of scan key descriptors */
- bool rs_bitmapscan; /* true if this is really a bitmap scan */
- bool rs_samplescan; /* true if this is really a sample scan */
+
+ /* scan current state */
+ bool rs_inited; /* false = scan not init'd yet */
+ BlockNumber rs_cblock; /* current block # in scan, if any */
+ Buffer rs_cbuf; /* current buffer in scan, if any */
+} StorageScanDescData;
+
+typedef struct HeapPageScanDescData
+{
bool rs_pageatatime; /* verify visibility page-at-a-time? */
- bool rs_allow_strat; /* allow or disallow use of access strategy */
- bool rs_allow_sync; /* allow or disallow use of syncscan */
- bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* total number of blocks in rel */
BlockNumber rs_startblock; /* block # to start at */
BlockNumber rs_numblocks; /* max number of blocks to scan */
+
/* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */
BufferAccessStrategy rs_strategy; /* access strategy for reads */
bool rs_syncscan; /* report location to syncscan logic? */
- /* scan current state */
- bool rs_inited; /* false = scan not init'd yet */
- HeapTupleData rs_ctup; /* current tuple in scan, if any */
- BlockNumber rs_cblock; /* current block # in scan, if any */
- Buffer rs_cbuf; /* current buffer in scan, if any */
- /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- ParallelHeapScanDesc rs_parallel; /* parallel scan information */
-
/* these fields only used in page-at-a-time mode and for bitmap scans */
int rs_cindex; /* current tuple's index in vistuples */
int rs_ntuples; /* number of visible tuples on page */
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]; /* their offsets */
+} HeapPageScanDescData;
+
+typedef struct HeapScanDescData
+{
+ /* scan parameters */
+ StorageScanDescData rs_scan; /* */
+ HeapPageScanDescData rs_pagescan;
+ bool rs_bitmapscan; /* true if this is really a bitmap scan */
+ bool rs_samplescan; /* true if this is really a sample scan */
+ bool rs_allow_strat; /* allow or disallow use of access strategy */
+ bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
+
+ HeapTupleData rs_ctup; /* current tuple in scan, if any */
+
+ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+ ParallelHeapScanDesc rs_parallel; /* parallel scan information */
} HeapScanDescData;
/*
@@ -149,12 +164,12 @@ typedef struct ParallelIndexScanDescData
char ps_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
} ParallelIndexScanDescData;
-/* Struct for heap-or-index scans of system tables */
+/* Struct for storage-or-index scans of system tables */
typedef struct SysScanDescData
{
Relation heap_rel; /* catalog being scanned */
Relation irel; /* NULL if doing heap scan */
- HeapScanDesc scan; /* only valid in heap-scan case */
+ StorageScanDesc scan; /* only valid in storage-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
index 7d70de4e33..ff28b4dfec 100644
--- a/src/include/access/storage_common.h
+++ b/src/include/access/storage_common.h
@@ -27,7 +27,6 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
-typedef void *StorageScanDesc;
/*
* slot storage routine functions
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index e79a24055d..5edaf84f49 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -40,29 +40,31 @@ typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool n
bool *specConflict, List *arbiterIndexes);
-extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
-
-extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
-extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
-
-extern void storage_endscan(HeapScanDesc scan);
-extern void storage_rescan(HeapScanDesc scan, ScanKey key);
-extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+extern StorageScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+extern ParallelHeapScanDesc storageam_get_parallelheapscandesc(StorageScanDesc sscan);
+extern HeapPageScanDesc storageam_get_heappagescandesc(StorageScanDesc sscan);
+extern void storage_setscanlimits(StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern StorageScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern StorageScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern StorageScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(StorageScanDesc scan);
+extern void storage_rescan(StorageScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(StorageScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+extern void storage_update_snapshot(StorageScanDesc scan, Snapshot snapshot);
-extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
-extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_getnext(StorageScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(StorageScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+extern StorageTuple storage_fetch_tuple_from_offset(StorageScanDesc sscan, BlockNumber blkno, OffsetNumber offset);
extern void storage_get_latest_tid(Relation relation,
Snapshot snapshot,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index bda6b46a94..721c901486 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -84,32 +84,37 @@ typedef void (*SpeculativeAbort_function) (Relation rel,
typedef void (*RelationSync_function) (Relation relation);
-typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
-typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+typedef StorageScanDesc(*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef ParallelHeapScanDesc (*ScanGetParallelheapscandesc_function) (StorageScanDesc scan);
+typedef HeapPageScanDesc(*ScanGetHeappagescandesc_function) (StorageScanDesc scan);
+
+typedef void (*ScanSetlimits_function) (StorageScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
/* must return a TupleTableSlot? */
-typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+typedef StorageTuple(*ScanGetnext_function) (StorageScanDesc scan,
ScanDirection direction);
-typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (StorageScanDesc scan,
ScanDirection direction, TupleTableSlot *slot);
+typedef StorageTuple(*ScanFetchTupleFromOffset_function) (StorageScanDesc scan,
+ BlockNumber blkno, OffsetNumber offset);
-typedef void (*ScanEnd_function) (HeapScanDesc scan);
+typedef void (*ScanEnd_function) (StorageScanDesc scan);
-typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
-typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+typedef void (*ScanGetpage_function) (StorageScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (StorageScanDesc scan, ScanKey key, bool set_params,
bool allow_strat, bool allow_sync, bool allow_pagemode);
-typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+typedef void (*ScanUpdateSnapshot_function) (StorageScanDesc scan, Snapshot snapshot);
typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
@@ -160,9 +165,12 @@ typedef struct StorageAmRoutine
/* Operations on relation scans */
ScanBegin_function scan_begin;
+ ScanGetParallelheapscandesc_function scan_get_parallelheapscandesc;
+ ScanGetHeappagescandesc_function scan_get_heappagescandesc;
ScanSetlimits_function scansetlimits;
ScanGetnext_function scan_getnext;
ScanGetnextSlot_function scan_getnextslot;
+ ScanFetchTupleFromOffset_function scan_fetch_tuple_from_offset;
ScanEnd_function scan_end;
ScanGetpage_function scan_getpage;
ScanRescan_function scan_rescan;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a956138e4a..cdaea6c545 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1112,7 +1112,7 @@ typedef struct ScanState
{
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
- HeapScanDesc ss_currentScanDesc;
+ StorageScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
@@ -1133,6 +1133,7 @@ typedef struct SeqScanState
typedef struct SampleScanState
{
ScanState ss;
+ HeapPageScanDesc pagescan;
List *args; /* expr states for TABLESAMPLE params */
ExprState *repeatable; /* expr state for REPEATABLE expr */
/* use struct pointer to avoid including tsmapi.h here */
@@ -1361,6 +1362,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
+ HeapPageScanDesc pagescan;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
--
2.15.0.windows.1
[application/octet-stream] 0001-Change-Create-Access-method-to-include-storage-handl.patch (9.3K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/4-0001-Change-Create-Access-method-to-include-storage-handl.patch)
download | inline diff:
From 61f7fa4ee40432796985ff9b7ea554c009336034 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 29 Aug 2017 19:45:30 +1000
Subject: [PATCH 1/8] Change Create Access method to include storage handler
Add the support of storage handler as an access method
---
src/backend/commands/amcmds.c | 17 ++++++++++++++---
src/backend/parser/gram.y | 11 +++++++++--
src/backend/utils/adt/pseudotypes.c | 1 +
src/include/catalog/pg_am.h | 1 +
src/include/catalog/pg_proc.h | 4 ++++
src/include/catalog/pg_type.h | 2 ++
src/test/regress/expected/opr_sanity.out | 19 ++++++++++++++++---
src/test/regress/sql/opr_sanity.sql | 16 +++++++++++++---
8 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 7e0a9aa0fd..33079c1c16 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -29,7 +29,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -72,7 +72,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -225,6 +225,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_STORAGE:
+ return "STORAGE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -239,7 +241,7 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
static const Oid funcargtypes[1] = {INTERNALOID};
@@ -263,6 +265,15 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
NameListToString(handler_name),
"index_am_handler")));
break;
+ /* XXX refactor duplicate error */
+ case AMTYPE_STORAGE:
+ if (get_func_rettype(handlerOid) != STORAGE_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(handler_name),
+ "storage_am_handler")));
+ break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ebfc94f896..25f397be6a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -321,6 +321,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> OptSchemaName
%type <list> OptSchemaEltList
+%type <chr> am_type
+
%type <boolean> TriggerForSpec TriggerForType
%type <ival> TriggerActionTime
%type <list> TriggerEvents TriggerOneEvent
@@ -5279,16 +5281,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | STORAGE { $$ = AMTYPE_STORAGE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index be793539a3..0a7e0a33e8 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(storage_am_handler);
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index e021f5b894..dd9c263ade 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -59,6 +59,7 @@ typedef FormData_pg_am *Form_pg_am;
* ----------------
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_STORAGE 's' /* storage access method */
/* ----------------
* initial contents of pg_am
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c969375981..1449a9cfd8 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3877,6 +3877,10 @@ DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f
DESCR("I/O");
DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3425 ( storage_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3998 "2275" _null_ _null_ _null_ _null_ _null_ storage_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3426 ( storage_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3998" _null_ _null_ _null_ _null_ _null_ storage_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e3551440a0..750b8b4533 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -708,6 +708,8 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
#define FDW_HANDLEROID 3115
DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
+DATA(insert OID = 3998 ( storage_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 storage_am_handler_in storage_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define STORAGE_AM_HANDLEROID 3998
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 684f7f20a8..3113966415 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1713,11 +1713,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for storage amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e8fdf8454d..bb1570c94f 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1155,15 +1155,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for storage amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'storage_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
--
2.15.0.windows.1
[application/octet-stream] 0002-Storage-AM-folder-and-init-functions.patch (11.0K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/5-0002-Storage-AM-folder-and-init-functions.patch)
download | inline diff:
From faa7a8c667484ee8841b131e6b6b59a24be21d23 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:41:15 +1000
Subject: [PATCH 2/8] Storage AM folder and init functions
---
src/backend/access/Makefile | 2 +-
src/backend/access/heap/Makefile | 3 +-
src/backend/access/heap/heapam_storage.c | 33 ++++++++++
src/backend/access/storage/Makefile | 17 +++++
src/backend/access/storage/storageam.c | 15 +++++
src/backend/access/storage/storageamapi.c | 103 ++++++++++++++++++++++++++++++
src/include/access/storageamapi.h | 39 +++++++++++
src/include/catalog/pg_am.h | 3 +
src/include/catalog/pg_proc.h | 5 ++
src/include/nodes/nodes.h | 1 +
10 files changed, 219 insertions(+), 2 deletions(-)
create mode 100644 src/backend/access/heap/heapam_storage.c
create mode 100644 src/backend/access/storage/Makefile
create mode 100644 src/backend/access/storage/storageam.c
create mode 100644 src/backend/access/storage/storageamapi.c
create mode 100644 src/include/access/storageamapi.h
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index bd93a6a8d1..e72ad6c86c 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -9,6 +9,6 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
SUBDIRS = brin common gin gist hash heap index nbtree rmgrdesc spgist \
- tablesample transam
+ storage tablesample transam
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index b83d496bcd..816f03a86f 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o
+OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o \
+ syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
new file mode 100644
index 0000000000..792e9cb436
--- /dev/null
+++ b/src/backend/access/heap/heapam_storage.c
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_storage.c
+ * heap storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_storage.c
+ *
+ *
+ * NOTES
+ * This file contains the heap_ routines which implement
+ * the POSTGRES heap access method used for all POSTGRES
+ * relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/storageamapi.h"
+#include "utils/builtins.h"
+
+
+Datum
+heapam_storage_handler(PG_FUNCTION_ARGS)
+{
+ StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+
+ PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/access/storage/Makefile b/src/backend/access/storage/Makefile
new file mode 100644
index 0000000000..2a05c7ce66
--- /dev/null
+++ b/src/backend/access/storage/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for access/storage
+#
+# IDENTIFICATION
+# src/backend/access/storage/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/storage
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = storageam.o storageamapi.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c
new file mode 100644
index 0000000000..8541c75782
--- /dev/null
+++ b/src/backend/access/storage/storageam.c
@@ -0,0 +1,15 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.c
+ * storage access method code
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/storage/storageam.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
diff --git a/src/backend/access/storage/storageamapi.c b/src/backend/access/storage/storageamapi.c
new file mode 100644
index 0000000000..bcbe14588b
--- /dev/null
+++ b/src/backend/access/storage/storageamapi.c
@@ -0,0 +1,103 @@
+/*----------------------------------------------------------------------
+ *
+ * storageamapi.c
+ * Support routines for API for Postgres storage access methods
+ *
+ * FIXME: looks like this should be in amapi.c.
+ *
+ * Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ * src/backend/access/heap/storageamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/storageamapi.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * GetStorageAmRoutine
+ * Call the specified access method handler routine to get its
+ * StorageAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+StorageAmRoutine *
+GetStorageAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ StorageAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (StorageAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, StorageAmRoutine))
+ elog(ERROR, "storage access method handler %u did not return a StorageAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/* A crock */
+StorageAmRoutine *
+GetHeapamStorageAmRoutine(void)
+{
+ Datum datum;
+ static StorageAmRoutine * HeapamStorageAmRoutine = NULL;
+
+ if (HeapamStorageAmRoutine == NULL)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ datum = OidFunctionCall0(HEAPAM_STORAGE_AM_HANDLER_OID);
+ HeapamStorageAmRoutine = (StorageAmRoutine *) DatumGetPointer(datum);
+ MemoryContextSwitchTo(oldcxt);
+ }
+
+ return HeapamStorageAmRoutine;
+}
+
+/*
+ * GetStorageAmRoutineByAmId - look up the handler of the storage access
+ * method with the given OID, and get its StorageAmRoutine struct.
+ */
+StorageAmRoutine *
+GetStorageAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a storage access method */
+ if (amform->amtype != AMTYPE_STORAGE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "STORAGE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("storage access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetStorageAmRoutine(amhandler);
+}
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
new file mode 100644
index 0000000000..6fae4eea5c
--- /dev/null
+++ b/src/include/access/storageamapi.h
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------
+ *
+ * storageamapi.h
+ * API for Postgres storage access methods
+ *
+ * Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * src/include/access/storageamapi.h
+ *---------------------------------------------------------------------
+ */
+#ifndef STORAGEAMAPI_H
+#define STORAGEAMAPI_H
+
+#include "nodes/nodes.h"
+#include "fmgr.h"
+
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+/*
+ * API struct for a storage AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ *
+ * XXX currently all functions are together in a single struct. Would it be
+ * worthwhile to split the slot-accessor functions to a different struct?
+ * That way, MinimalTuple could be handled without a complete StorageAmRoutine
+ * for them -- it'd only have a few functions in TupleTableSlotAmRoutine or so.
+ */
+typedef struct StorageAmRoutine
+{
+ NodeTag type;
+
+} StorageAmRoutine;
+
+extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
+extern StorageAmRoutine * GetStorageAmRoutineByAmId(Oid amoid);
+extern StorageAmRoutine * GetHeapamStorageAmRoutine(void);
+
+#endif /* STORAGEAMAPI_H */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index dd9c263ade..2c3e33c104 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -84,5 +84,8 @@ DESCR("SP-GiST index access method");
DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
+DATA(insert OID = 4001 ( heapam heapam_storage_handler s ));
+DESCR("heapam storage access method");
+#define HEAPAM_STORAGE_AM_OID 4001
#endif /* PG_AM_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 1449a9cfd8..b26b5900de 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -558,6 +558,11 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
+/* Storage access method handlers */
+DATA(insert OID = 4002 ( heapam_storage_handler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3998 "2281" _null_ _null_ _null_ _null_ _null_ heapam_storage_handler _null_ _null_ _null_ ));
+DESCR("row-oriented storage access method handler");
+#define HEAPAM_STORAGE_AM_HANDLER_OID 4002
+
/* Index access method handlers */
DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
DESCR("btree index access method handler");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c5b5115f5b..927126f729 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -499,6 +499,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_StorageAmRoutine, /* in access/storageamapi.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo /* in utils/rel.h */
} NodeTag;
--
2.15.0.windows.1
[application/octet-stream] 0003-Adding-storageam-hanlder-to-relation-structure.patch (7.0K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/6-0003-Adding-storageam-hanlder-to-relation-structure.patch)
download | inline diff:
From 4166f6f7423bb5a0aa712d47669a4bbe0da9f083 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Wed, 30 Aug 2017 12:49:46 +1000
Subject: [PATCH 3/8] Adding storageam hanlder to relation structure
And also the necessary functions to initialize
the storageam handler
---
src/backend/utils/cache/relcache.c | 119 ++++++++++++++++++++++++++++++++++++-
src/include/utils/rel.h | 12 ++++
src/include/utils/relcache.h | 2 +
3 files changed, 130 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 12a5f157c0..dd70507c73 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -35,6 +35,7 @@
#include "access/multixact.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1371,10 +1372,27 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ Assert(relation->rd_rel->relkind != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_SEQUENCE:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW: /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
+ RelationInitStorageAccessInfo(relation);
+ break;
+ default:
+ /* nothing to do in other cases */
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1871,6 +1889,71 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the StorageAmRoutine for a relation
+ *
+ * relation's rd_amhandler and rd_indexcxt (XXX?) must be valid already.
+ */
+static void
+InitStorageAmRoutine(Relation relation)
+{
+ StorageAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetStorageAmRoutine(relation->rd_amhandler);
+
+ /* XXX do we need a separate memory context for this? */
+ /* OK, now transfer the data into cache context */
+ cached = (StorageAmRoutine *) MemoryContextAlloc(CacheMemoryContext,
+ sizeof(StorageAmRoutine));
+ memcpy(cached, tmp, sizeof(StorageAmRoutine));
+ relation->rd_stamroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
+ * Initialize storage-access-method support data for a heap relation
+ */
+void
+RelationInitStorageAccessInfo(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ /*
+ * Relations that don't have a catalogued storage access method use the
+ * standard heapam module; otherwise a catalog lookup is in order.
+ */
+ if (!OidIsValid(relation->rd_rel->relam))
+ {
+ relation->rd_amhandler = HEAPAM_STORAGE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the storage access method, save the OID of its handler
+ * function.
+ */
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the storage AM's API struct
+ */
+ InitStorageAmRoutine(relation);
+}
/*
* formrdesc
@@ -2029,6 +2112,11 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
RelationInitPhysicalAddr(relation);
+ /*
+ * initialize the storage am handler
+ */
+ relation->rd_stamroutine = GetHeapamStorageAmRoutine();
+
/*
* initialize the rel-has-index flag, using hardwired knowledge
*/
@@ -2357,6 +2445,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_pubactions);
if (relation->rd_options)
pfree(relation->rd_options);
+ if (relation->rd_stamroutine)
+ pfree(relation->rd_stamroutine);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
if (relation->rd_indexcxt)
@@ -3371,6 +3461,14 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_VIEW || /* Not exactly the storage, but underlying
+ * tuple access, it is required */
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_TOASTVALUE)
+ RelationInitStorageAccessInfo(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3892,6 +3990,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_stamroutine == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW ||
+ relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+ {
+ RelationInitStorageAccessInfo(relation);
+ Assert(relation->rd_stamroutine != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5610,6 +5720,9 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load storage AM stuff */
+ RelationInitStorageAccessInfo(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 68fd6fbd54..72a15db9a4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -160,6 +160,12 @@ typedef struct RelationData
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
+ /*
+ * Underlying storage support
+ */
+ Oid rd_storageam; /* OID of storage AM handler function */
+ struct StorageAmRoutine *rd_stamroutine; /* storage AM's API struct */
+
/*
* index access support info (used only for an index relation)
*
@@ -436,6 +442,12 @@ typedef struct ViewOptions
*/
#define RelationGetDescr(relation) ((relation)->rd_att)
+/*
+ * RelationGetStorageRoutine
+ * Returns the storage AM routine for a relation.
+ */
+#define RelationGetStorageRoutine(relation) ((relation)->rd_stamroutine)
+
/*
* RelationGetRelationName
* Returns the rel's name.
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 29c6d9bae3..8f7529a7c5 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -76,6 +76,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitStorageAccessInfo(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
--
2.15.0.windows.1
[application/octet-stream] 0004-Adding-tuple-visibility-function-to-storage-AM.patch (67.0K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/7-0004-Adding-tuple-visibility-function-to-storage-AM.patch)
download | inline diff:
From 662700ceb5bbe8f0c46448fdddbc18a603d54a90 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Fri, 1 Dec 2017 14:49:18 +1100
Subject: [PATCH 4/8] Adding tuple visibility function to storage AM
Tuple visibility functions are now part of the
heap storage AM routine. The visibilty execution
procedure is changed accoridngly.
The snapshot satifies function is changed to an
enum to represent what type of snapshot is it
and this enum value is used to call the corresponding
visibilty function from the storage AM when the
visibilty of the tuple is required.
The common code is that is part of both server
and pluggable storages is now moved into storage_common.c
and storage_common.h files.
---
contrib/pg_visibility/pg_visibility.c | 11 +-
contrib/pgrowlocks/pgrowlocks.c | 7 +-
contrib/pgstattuple/pgstatapprox.c | 7 +-
contrib/pgstattuple/pgstattuple.c | 3 +-
src/backend/access/heap/Makefile | 2 +-
src/backend/access/heap/heapam.c | 61 ++-
src/backend/access/heap/heapam_storage.c | 10 +
.../tqual.c => access/heap/heapam_visibility.c} | 448 +++++----------------
src/backend/access/heap/pruneheap.c | 4 +-
src/backend/access/index/genam.c | 4 +-
src/backend/access/storage/Makefile | 2 +-
src/backend/access/storage/storage_common.c | 277 +++++++++++++
src/backend/catalog/index.c | 6 +-
src/backend/commands/analyze.c | 6 +-
src/backend/commands/cluster.c | 3 +-
src/backend/commands/vacuumlazy.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 7 +-
src/backend/executor/nodeSamplescan.c | 3 +-
src/backend/replication/logical/snapbuild.c | 6 +-
src/backend/storage/lmgr/predicate.c | 2 +-
src/backend/utils/adt/ri_triggers.c | 2 +-
src/backend/utils/time/Makefile | 2 +-
src/backend/utils/time/snapmgr.c | 10 +-
src/include/access/storage_common.h | 98 +++++
src/include/access/storageamapi.h | 12 +
src/include/storage/bufmgr.h | 5 +-
src/include/utils/snapshot.h | 14 +-
src/include/utils/tqual.h | 54 +--
29 files changed, 607 insertions(+), 465 deletions(-)
rename src/backend/{utils/time/tqual.c => access/heap/heapam_visibility.c} (80%)
create mode 100644 src/backend/access/storage/storage_common.c
create mode 100644 src/include/access/storage_common.h
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 2cc9575d9f..01ff3fed5d 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -11,6 +11,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageamapi.h"
#include "access/visibilitymap.h"
#include "catalog/pg_type.h"
#include "catalog/storage_xlog.h"
@@ -51,7 +52,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd);
static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible,
bool all_frozen);
static void record_corrupt_item(corrupt_items *items, ItemPointer tid);
-static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin,
+static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin,
Buffer buffer);
static void check_relation_relkind(Relation rel);
@@ -656,7 +657,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
* the tuple to be all-visible.
*/
if (check_visible &&
- !tuple_all_visible(&tuple, OldestXmin, buffer))
+ !tuple_all_visible(rel, &tuple, OldestXmin, buffer))
{
TransactionId RecomputedOldestXmin;
@@ -681,7 +682,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
else
{
OldestXmin = RecomputedOldestXmin;
- if (!tuple_all_visible(&tuple, OldestXmin, buffer))
+ if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer))
record_corrupt_item(items, &tuple.t_self);
}
}
@@ -739,12 +740,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid)
* The buffer should contain the tuple and should be locked and pinned.
*/
static bool
-tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
+tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
{
HTSV_Result state;
TransactionId xmin;
- state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer);
+ state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer);
if (state != HEAPTUPLE_LIVE)
return false; /* all-visible implies live */
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index eabca65bd2..830e74fd07 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -26,6 +26,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
@@ -149,9 +150,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- htsu = HeapTupleSatisfiesUpdate(tuple,
- GetCurrentCommandId(false),
- scan->rs_cbuf);
+ htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple,
+ GetCurrentCommandId(false),
+ scan->rs_cbuf);
xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
infomask = tuple->t_data->t_infomask;
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 5bf06138a5..284eabc970 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -12,12 +12,13 @@
*/
#include "postgres.h"
-#include "access/visibilitymap.h"
#include "access/transam.h"
+#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/multixact.h"
#include "access/htup_details.h"
#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -26,7 +27,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
-#include "commands/vacuum.h"
+
PG_FUNCTION_INFO_V1(pgstattuple_approx);
PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5);
@@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat)
* We count live and dead tuples, but we also need to add up
* others in order to feed vac_estimate_reltuples.
*/
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_RECENTLY_DEAD:
misc_count++;
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 7ca1bb24d2..e098202f84 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Buffer buffer;
pgstattuple_type stat = {0};
SnapshotData SnapshotDirty;
+ StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
@@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf))
+ if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf))
{
stat.tuple_len += tuple->t_len;
stat.tuple_count++;
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index 816f03a86f..f5c628395b 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o \
+OBJS = heapam.o hio.o heapam_storage.o heapam_visibility.o pruneheap.o rewriteheap.o \
syncscan.o tuptoaster.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3acef279f4..d63afbaf69 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -45,6 +45,7 @@
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/relscan.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -438,7 +439,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page)
if (all_visible)
valid = true;
else
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -653,7 +654,8 @@ heapgettup(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- valid = HeapTupleSatisfiesVisibility(tuple,
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
snapshot,
scan->rs_cbuf);
@@ -841,6 +843,7 @@ heapgettup_pagemode(HeapScanDesc scan,
lineindex = scan->rs_cindex + 1;
}
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -885,6 +888,7 @@ heapgettup_pagemode(HeapScanDesc scan,
page = scan->rs_cblock; /* current page */
}
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -954,23 +958,31 @@ heapgettup_pagemode(HeapScanDesc scan,
/*
* if current tuple qualifies, return it.
*/
- if (key != NULL)
+ if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf))
{
- bool valid;
+ /*
+ * if current tuple qualifies, return it.
+ */
+ if (key != NULL)
+ {
+ bool valid;
- HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
- nkeys, key, valid);
- if (valid)
+ HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd),
+ nkeys, key, valid);
+ if (valid)
+ {
+ scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+ return;
+ }
+ }
+ else
{
scan->rs_cindex = lineindex;
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
return;
}
}
- else
- {
- scan->rs_cindex = lineindex;
- return;
- }
/*
* otherwise move to the next item on the page
@@ -982,6 +994,12 @@ heapgettup_pagemode(HeapScanDesc scan,
++lineindex;
}
+ /*
+ * if we get here, it means we've exhausted the items on this page and
+ * it's time to move to the next.
+ */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
/*
* if we get here, it means we've exhausted the items on this page and
* it's time to move to the next.
@@ -1039,6 +1057,7 @@ heapgettup_pagemode(HeapScanDesc scan,
heapgetpage(scan, page);
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
dp = BufferGetPage(scan->rs_cbuf);
TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp);
lines = scan->rs_ntuples;
@@ -1831,7 +1850,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
pgstat_count_heap_getnext(scan->rs_rd);
- return &(scan->rs_ctup);
+ return heap_copytuple(&(scan->rs_ctup));
}
/*
@@ -1950,7 +1969,7 @@ heap_fetch(Relation relation,
/*
* check time qualification of tuple, then release lock
*/
- valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer);
if (valid)
PredicateLockTuple(relation, tuple, snapshot);
@@ -2097,7 +2116,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
/* If it's visible per the snapshot, we must return it */
- valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, heapTuple,
buffer, snapshot);
/* reset to original, non-redirected, tid */
@@ -2271,7 +2290,7 @@ heap_get_latest_tid(Relation relation,
* Check time qualification of tuple; if visible, set it as the new
* result candidate.
*/
- valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer);
CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
if (valid)
*tid = ctid;
@@ -3097,7 +3116,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_self = *tid;
l1:
- result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer);
if (result == HeapTupleInvisible)
{
@@ -3208,7 +3227,7 @@ l1:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -3668,7 +3687,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
checked_lockers = false;
locker_remains = false;
- result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer);
/* see below about the "no wait" case */
Assert(result != HeapTupleBeingUpdated || wait);
@@ -3849,7 +3868,7 @@ l2:
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for transaction-snapshot mode RI updates */
- if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+ if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer))
result = HeapTupleUpdated;
}
@@ -4600,7 +4619,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_tableOid = RelationGetRelid(relation);
l3:
- result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+ result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 792e9cb436..2d3ed44baf 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -23,11 +23,21 @@
#include "access/storageamapi.h"
#include "utils/builtins.h"
+extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer);
+extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer);
+extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer);
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
StorageAmRoutine *amroutine = makeNode(StorageAmRoutine);
+ amroutine->snapshot_satisfies = HeapTupleSatisfies;
+
+ amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
+ amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
+
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/utils/time/tqual.c b/src/backend/access/heap/heapam_visibility.c
similarity index 80%
rename from src/backend/utils/time/tqual.c
rename to src/backend/access/heap/heapam_visibility.c
index a821e2eed1..3bfddae506 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/access/heap/heapam_visibility.c
@@ -1,7 +1,33 @@
/*-------------------------------------------------------------------------
*
- * tqual.c
- * POSTGRES "time qualification" code, ie, tuple visibility rules.
+ * heapam_visibility.c
+ * heapam access method visibility functions
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_visibility.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/heapam_xlog.h"
+#include "access/hio.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "storage/procarray.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+/*-------------------------------------------------------------------------
+ *
+ * POSTGRES "time qualification" code, ie, tuple visibility rules.
*
* NOTE: all the HeapTupleSatisfies routines will update the tuple's
* "hint" status bits if we see that the inserting or deleting transaction
@@ -45,108 +71,21 @@
* like HeapTupleSatisfiesSelf(), but includes open transactions
* HeapTupleSatisfiesVacuum()
* visible to any running transaction, used by VACUUM
- * HeapTupleSatisfiesNonVacuumable()
- * Snapshot-style API for HeapTupleSatisfiesVacuum
+ * HeapTupleSatisfiesNonVacuumable()
+ * Snapshot-style API for HeapTupleSatisfiesVacuum
* HeapTupleSatisfiesToast()
* visible unless part of interrupted vacuum, used for TOAST
* HeapTupleSatisfiesAny()
* all tuples are visible
*
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/backend/utils/time/tqual.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/multixact.h"
-#include "access/subtrans.h"
-#include "access/transam.h"
-#include "access/xact.h"
-#include "access/xlog.h"
-#include "storage/bufmgr.h"
-#include "storage/procarray.h"
-#include "utils/builtins.h"
-#include "utils/combocid.h"
-#include "utils/snapmgr.h"
-#include "utils/tqual.h"
-
-
-/* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
-
-
-/*
- * SetHintBits()
- *
- * Set commit/abort hint bits on a tuple, if appropriate at this time.
- *
- * It is only safe to set a transaction-committed hint bit if we know the
- * transaction's commit record is guaranteed to be flushed to disk before the
- * buffer, or if the table is temporary or unlogged and will be obliterated by
- * a crash anyway. We cannot change the LSN of the page here, because we may
- * hold only a share lock on the buffer, so we can only use the LSN to
- * interlock this if the buffer's LSN already is newer than the commit LSN;
- * otherwise we have to just refrain from setting the hint bit until some
- * future re-examination of the tuple.
- *
- * We can always set hint bits when marking a transaction aborted. (Some
- * code in heapam.c relies on that!)
- *
- * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
- * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
- * synchronous commits and didn't move tuples that weren't previously
- * hinted. (This is not known by this subroutine, but is applied by its
- * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
- * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
- * support in-place update from pre-9.0 databases.
- *
- * Normal commits may be asynchronous, so for those we need to get the LSN
- * of the transaction and then check whether this is flushed.
- *
- * The caller should pass xid as the XID of the transaction to check, or
- * InvalidTransactionId if no check is needed.
- */
-static inline void
-SetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- if (TransactionIdIsValid(xid))
- {
- /* NB: xid must be known committed here! */
- XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
-
- if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
- BufferGetLSNAtomic(buffer) < commitLSN)
- {
- /* not flushed and no LSN interlock, so don't set hint */
- return;
- }
- }
-
- tuple->t_infomask |= infomask;
- MarkBufferDirtyHint(buffer, true);
-}
-
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
+ * -------------------------------------------------------------------------
*/
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
+extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer);
+extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer);
+extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
+ Buffer buffer);
/*
* HeapTupleSatisfiesSelf
@@ -172,9 +111,10 @@ HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
* (Xmax != my-transaction && the row was deleted by another transaction
* Xmax is not committed))) that has not been committed
*/
-bool
-HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
+static bool
+HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -342,8 +282,8 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
* HeapTupleSatisfiesAny
* Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
*/
-bool
-HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
+static bool
+HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer)
{
return true;
}
@@ -362,10 +302,11 @@ HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
* Among other things, this means you can't do UPDATEs of rows in a TOAST
* table.
*/
-bool
-HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
+static bool
+HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -457,9 +398,10 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
* distinguish that case must test for it themselves.)
*/
HTSU_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
+HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -735,10 +677,11 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
* on the insertion without aborting the whole transaction, the associated
* token is also returned in snapshot->speculativeToken.
*/
-bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
+static bool
+HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -959,10 +902,11 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
* inserting/deleting transaction was still running --- which was more cycles
* and more contention on the PGXACT array.
*/
-bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
+static bool
+HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -1161,9 +1105,10 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
* even if we see that the deleting transaction has committed.
*/
HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
+HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
Assert(ItemPointerIsValid(&htup->t_self));
@@ -1392,258 +1337,24 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
return HEAPTUPLE_DEAD;
}
-
/*
* HeapTupleSatisfiesNonVacuumable
*
- * True if tuple might be visible to some transaction; false if it's
- * surely dead to everyone, ie, vacuumable.
+ * True if tuple might be visible to some transaction; false if it's
+ * surely dead to everyone, ie, vacuumable.
*
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
- * snapshot->xmin must have been set up with the xmin horizon to use.
+ * This is an interface to HeapTupleSatisfiesVacuum that meets the
+ * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * snapshot->xmin must have been set up with the xmin horizon to use.
*/
-bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
+static bool
+HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot,
Buffer buffer)
{
return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
!= HEAPTUPLE_DEAD;
}
-
-/*
- * HeapTupleIsSurelyDead
- *
- * Cheaply determine whether a tuple is surely dead to all onlookers.
- * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
- * tuple has just been tested by another visibility routine (usually
- * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
- * should already be set. We assume that if no hint bits are set, the xmin
- * or xmax transaction is still running. This is therefore faster than
- * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
- * It's okay to return false when in doubt, but we must return true only
- * if the tuple is removable.
- */
-bool
-HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
-{
- HeapTupleHeader tuple = htup->t_data;
-
- Assert(ItemPointerIsValid(&htup->t_self));
- Assert(htup->t_tableOid != InvalidOid);
-
- /*
- * If the inserting transaction is marked invalid, then it aborted, and
- * the tuple is definitely dead. If it's marked neither committed nor
- * invalid, then we assume it's still alive (since the presumption is that
- * all relevant hint bits were just set moments ago).
- */
- if (!HeapTupleHeaderXminCommitted(tuple))
- return HeapTupleHeaderXminInvalid(tuple) ? true : false;
-
- /*
- * If the inserting transaction committed, but any deleting transaction
- * aborted, the tuple is still alive.
- */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return false;
-
- /*
- * If the XMAX is just a lock, the tuple is still alive.
- */
- if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- return false;
-
- /*
- * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
- * know without checking pg_multixact.
- */
- if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
- return false;
-
- /* If deleter isn't known to have committed, assume it's still running. */
- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
- return false;
-
- /* Deleter committed, so tuple is dead if the XID is old enough. */
- return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
-}
-
-/*
- * XidInMVCCSnapshot
- * Is the given XID still-in-progress according to the snapshot?
- *
- * Note: GetSnapshotData never stores either top xid or subxids of our own
- * backend into a snapshot, so these xids will not be reported as "running"
- * by this function. This is OK for current uses, because we always check
- * TransactionIdIsCurrentTransactionId first, except when it's known the
- * XID could not be ours anyway.
- */
-bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
-{
- uint32 i;
-
- /*
- * Make a quick range check to eliminate most XIDs without looking at the
- * xip arrays. Note that this is OK even if we convert a subxact XID to
- * its parent below, because a subxact with XID < xmin has surely also got
- * a parent with XID < xmin, while one with XID >= xmax must belong to a
- * parent that was not yet committed at the time of this snapshot.
- */
-
- /* Any xid < xmin is not in-progress */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- /* Any xid >= xmax is in-progress */
- if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
- return true;
-
- /*
- * Snapshot information is stored slightly differently in snapshots taken
- * during recovery.
- */
- if (!snapshot->takenDuringRecovery)
- {
- /*
- * If the snapshot contains full subxact data, the fastest way to
- * check things is just to compare the given XID against both subxact
- * XIDs and top-level XIDs. If the snapshot overflowed, we have to
- * use pg_subtrans to convert a subxact XID to its parent XID, but
- * then we need only look at top-level XIDs not subxacts.
- */
- if (!snapshot->suboverflowed)
- {
- /* we have full data, so search subxip */
- int32 j;
-
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
-
- /* not there, fall through to search xip[] */
- }
- else
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- for (i = 0; i < snapshot->xcnt; i++)
- {
- if (TransactionIdEquals(xid, snapshot->xip[i]))
- return true;
- }
- }
- else
- {
- int32 j;
-
- /*
- * In recovery we store all xids in the subxact array because it is by
- * far the bigger array, and we mostly don't know which xids are
- * top-level and which are subxacts. The xip array is empty.
- *
- * We start by searching subtrans, if we overflowed.
- */
- if (snapshot->suboverflowed)
- {
- /*
- * Snapshot overflowed, so convert xid to top-level. This is safe
- * because we eliminated too-old XIDs above.
- */
- xid = SubTransGetTopmostTransaction(xid);
-
- /*
- * If xid was indeed a subxact, we might now have an xid < xmin,
- * so recheck to avoid an array scan. No point in rechecking
- * xmax.
- */
- if (TransactionIdPrecedes(xid, snapshot->xmin))
- return false;
- }
-
- /*
- * We now have either a top-level xid higher than xmin or an
- * indeterminate xid. We don't know whether it's top level or subxact
- * but it doesn't matter. If it's present, the xid is visible.
- */
- for (j = 0; j < snapshot->subxcnt; j++)
- {
- if (TransactionIdEquals(xid, snapshot->subxip[j]))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * Is the tuple really only locked? That is, is it not updated?
- *
- * It's easy to check just infomask bits if the locker is not a multi; but
- * otherwise we need to verify that the updating transaction has not aborted.
- *
- * This function is here because it follows the same time qualification rules
- * laid out at the top of this file.
- */
-bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
-{
- TransactionId xmax;
-
- /* if there's no valid Xmax, then there's obviously no update either */
- if (tuple->t_infomask & HEAP_XMAX_INVALID)
- return true;
-
- if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
- return true;
-
- /* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
- return true;
-
- /*
- * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
- * necessarily have been updated
- */
- if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
- return false;
-
- /* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
-
- /* not LOCKED_ONLY, so it has to have an xmax */
- Assert(TransactionIdIsValid(xmax));
-
- if (TransactionIdIsCurrentTransactionId(xmax))
- return false;
- if (TransactionIdIsInProgress(xmax))
- return false;
- if (TransactionIdDidCommit(xmax))
- return false;
-
- /*
- * not current, not in progress, not committed -- must have aborted or
- * crashed
- */
- return true;
-}
-
/*
* check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
*/
@@ -1668,10 +1379,11 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
* dangerous to do so as the semantics of doing so during timetravel are more
* complicated than when dealing "only" with the present.
*/
-bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
+static bool
+HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot,
Buffer buffer)
{
+ HeapTuple htup = (HeapTuple) stup;
HeapTupleHeader tuple = htup->t_data;
TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
@@ -1805,3 +1517,35 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
else
return true;
}
+
+bool
+HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer)
+{
+ switch (snapshot->visibility_type)
+ {
+ case MVCC_VISIBILITY:
+ return HeapTupleSatisfiesMVCC(stup, snapshot, buffer);
+ break;
+ case SELF_VISIBILITY:
+ return HeapTupleSatisfiesSelf(stup, snapshot, buffer);
+ break;
+ case ANY_VISIBILITY:
+ return HeapTupleSatisfiesAny(stup, snapshot, buffer);
+ break;
+ case TOAST_VISIBILITY:
+ return HeapTupleSatisfiesToast(stup, snapshot, buffer);
+ break;
+ case DIRTY_VISIBILITY:
+ return HeapTupleSatisfiesDirty(stup, snapshot, buffer);
+ break;
+ case HISTORIC_MVCC_VISIBILITY:
+ return HeapTupleSatisfiesHistoricMVCC(stup, snapshot, buffer);
+ break;
+ case NON_VACUUMABLE_VISIBILTY:
+ return HeapTupleSatisfiesNonVacuumable(stup, snapshot, buffer);
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 9f33e0ce07..ed40357976 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)
+ if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)
== HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
@@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
*/
tupdead = recent_dead = false;
- switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer))
+ switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer))
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 05d7da001a..01321a2543 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->xs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
else
@@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Assert(BufferIsValid(scan->rs_cbuf));
/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf);
+ result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf);
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
}
return result;
diff --git a/src/backend/access/storage/Makefile b/src/backend/access/storage/Makefile
index 2a05c7ce66..321676820f 100644
--- a/src/backend/access/storage/Makefile
+++ b/src/backend/access/storage/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/storage
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = storageam.o storageamapi.o
+OBJS = storageam.o storageamapi.o storage_common.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/storage/storage_common.c b/src/backend/access/storage/storage_common.c
new file mode 100644
index 0000000000..3ede680c89
--- /dev/null
+++ b/src/backend/access/storage/storage_common.c
@@ -0,0 +1,277 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_common.c
+ * storage access method code that is common across all pluggable
+ * storage modules
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/storage/storage_common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/storage_common.h"
+#include "access/subtrans.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufmgr.h"
+#include "storage/procarray.h"
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
+SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
+
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
+
+
+/*
+ * Is the tuple really only locked? That is, is it not updated?
+ *
+ * It's easy to check just infomask bits if the locker is not a multi; but
+ * otherwise we need to verify that the updating transaction has not aborted.
+ *
+ * This function is here because it follows the same time qualification rules
+ * laid out at the top of this file.
+ */
+bool
+HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+{
+ TransactionId xmax;
+
+ /* if there's no valid Xmax, then there's obviously no update either */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return true;
+
+ if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ return true;
+
+ /* invalid xmax means no update */
+ if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ return true;
+
+ /*
+ * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
+ * necessarily have been updated
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
+ return false;
+
+ /* ... but if it's a multi, then perhaps the updating Xid aborted. */
+ xmax = HeapTupleGetUpdateXid(tuple);
+
+ /* not LOCKED_ONLY, so it has to have an xmax */
+ Assert(TransactionIdIsValid(xmax));
+
+ if (TransactionIdIsCurrentTransactionId(xmax))
+ return false;
+ if (TransactionIdIsInProgress(xmax))
+ return false;
+ if (TransactionIdDidCommit(xmax))
+ return false;
+
+ /*
+ * not current, not in progress, not committed -- must have aborted or
+ * crashed
+ */
+ return true;
+}
+
+
+/*
+ * HeapTupleIsSurelyDead
+ *
+ * Cheaply determine whether a tuple is surely dead to all onlookers.
+ * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
+ * tuple has just been tested by another visibility routine (usually
+ * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
+ * should already be set. We assume that if no hint bits are set, the xmin
+ * or xmax transaction is still running. This is therefore faster than
+ * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG.
+ * It's okay to return false when in doubt, but we must return TRUE only
+ * if the tuple is removable.
+ */
+bool
+HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
+{
+ HeapTupleHeader tuple = htup->t_data;
+
+ Assert(ItemPointerIsValid(&htup->t_self));
+ Assert(htup->t_tableOid != InvalidOid);
+
+ /*
+ * If the inserting transaction is marked invalid, then it aborted, and
+ * the tuple is definitely dead. If it's marked neither committed nor
+ * invalid, then we assume it's still alive (since the presumption is that
+ * all relevant hint bits were just set moments ago).
+ */
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
+
+ /*
+ * If the inserting transaction committed, but any deleting transaction
+ * aborted, the tuple is still alive.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_INVALID)
+ return false;
+
+ /*
+ * If the XMAX is just a lock, the tuple is still alive.
+ */
+ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ return false;
+
+ /*
+ * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
+ * know without checking pg_multixact.
+ */
+ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ return false;
+
+ /* If deleter isn't known to have committed, assume it's still running. */
+ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+ return false;
+
+ /* Deleter committed, so tuple is dead if the XID is old enough. */
+ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin);
+}
+
+/*
+ * XidInMVCCSnapshot
+ * Is the given XID still-in-progress according to the snapshot?
+ *
+ * Note: GetSnapshotData never stores either top xid or subxids of our own
+ * backend into a snapshot, so these xids will not be reported as "running"
+ * by this function. This is OK for current uses, because we always check
+ * TransactionIdIsCurrentTransactionId first, except when it's known the
+ * XID could not be ours anyway.
+ */
+bool
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+{
+ uint32 i;
+
+ /*
+ * Make a quick range check to eliminate most XIDs without looking at the
+ * xip arrays. Note that this is OK even if we convert a subxact XID to
+ * its parent below, because a subxact with XID < xmin has surely also got
+ * a parent with XID < xmin, while one with XID >= xmax must belong to a
+ * parent that was not yet committed at the time of this snapshot.
+ */
+
+ /* Any xid < xmin is not in-progress */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ /* Any xid >= xmax is in-progress */
+ if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
+ return true;
+
+ /*
+ * Snapshot information is stored slightly differently in snapshots taken
+ * during recovery.
+ */
+ if (!snapshot->takenDuringRecovery)
+ {
+ /*
+ * If the snapshot contains full subxact data, the fastest way to
+ * check things is just to compare the given XID against both subxact
+ * XIDs and top-level XIDs. If the snapshot overflowed, we have to
+ * use pg_subtrans to convert a subxact XID to its parent XID, but
+ * then we need only look at top-level XIDs not subxacts.
+ */
+ if (!snapshot->suboverflowed)
+ {
+ /* we have full data, so search subxip */
+ int32 j;
+
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+
+ /* not there, fall through to search xip[] */
+ }
+ else
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ for (i = 0; i < snapshot->xcnt; i++)
+ {
+ if (TransactionIdEquals(xid, snapshot->xip[i]))
+ return true;
+ }
+ }
+ else
+ {
+ int32 j;
+
+ /*
+ * In recovery we store all xids in the subxact array because it is by
+ * far the bigger array, and we mostly don't know which xids are
+ * top-level and which are subxacts. The xip array is empty.
+ *
+ * We start by searching subtrans, if we overflowed.
+ */
+ if (snapshot->suboverflowed)
+ {
+ /*
+ * Snapshot overflowed, so convert xid to top-level. This is safe
+ * because we eliminated too-old XIDs above.
+ */
+ xid = SubTransGetTopmostTransaction(xid);
+
+ /*
+ * If xid was indeed a subxact, we might now have an xid < xmin,
+ * so recheck to avoid an array scan. No point in rechecking
+ * xmax.
+ */
+ if (TransactionIdPrecedes(xid, snapshot->xmin))
+ return false;
+ }
+
+ /*
+ * We now have either a top-level xid higher than xmin or an
+ * indeterminate xid. We don't know whether it's top level or subxact
+ * but it doesn't matter. If it's present, the xid is visible.
+ */
+ for (j = 0; j < snapshot->subxcnt; j++)
+ {
+ if (TransactionIdEquals(xid, snapshot->subxip[j]))
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0125c18bc1..ead8d2abdf 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2225,6 +2225,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
TransactionId OldestXmin;
BlockNumber root_blkno = InvalidBlockNumber;
OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+ StorageAmRoutine *method;
/*
* sanity checks
@@ -2280,6 +2281,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM);
}
+ method = heapRelation->rd_stamroutine;
scan = heap_beginscan_strat(heapRelation, /* relation */
snapshot, /* snapshot */
0, /* number of keys */
@@ -2360,8 +2362,8 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
- scan->rs_cbuf))
+ switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin,
+ scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead, we can ignore it */
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index f952b3c732..4411c1d4de 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1119,9 +1119,9 @@ acquire_sample_rows(Relation onerel, int elevel,
targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple.t_len = ItemIdGetLength(itemid);
- switch (HeapTupleSatisfiesVacuum(&targtuple,
- OldestXmin,
- targbuffer))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple,
+ OldestXmin,
+ targbuffer))
{
case HEAPTUPLE_LIVE:
sample_it = true;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48f1e6e2ad..dbcc5bc172 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
LockBuffer(buf, BUFFER_LOCK_SHARE);
- switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
+ switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 20ce431e46..04729e5c27 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -986,7 +986,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
tupgone = false;
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
@@ -2151,7 +2151,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf,
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
- switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
+ switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf))
{
case HEAPTUPLE_LIVE:
{
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index eb5bbb57ef..2c5c95d425 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -461,7 +461,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
ItemPointerSet(&loctup.t_self, page, offnum);
- valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+ valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer);
if (valid)
{
scan->rs_vistuples[ntup++] = offnum;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index afb83ed3ae..79c34a6e6c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -191,6 +191,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
*/
static void
ExecCheckHeapTupleVisible(EState *estate,
+ Relation rel,
HeapTuple tuple,
Buffer buffer)
{
@@ -202,7 +203,7 @@ ExecCheckHeapTupleVisible(EState *estate,
* Caller should be holding pin, but not lock.
*/
LockBuffer(buffer, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
+ if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
/*
* We should not raise a serialization failure if the conflict is
@@ -237,7 +238,7 @@ ExecCheckTIDVisible(EState *estate,
tuple.t_self = *tid;
if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
ReleaseBuffer(buffer);
}
@@ -1313,7 +1314,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a836e4..6a118d1883 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
else
{
/* Otherwise, we have to check the tuple individually. */
- return HeapTupleSatisfiesVisibility(tuple,
+ return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine,
+ tuple,
scan->rs_snapshot,
scan->rs_cbuf);
}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index ad65b9831d..86efe3a66b 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -376,7 +376,7 @@ static void
SnapBuildFreeSnapshot(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -434,7 +434,7 @@ void
SnapBuildSnapDecRefcount(Snapshot snap)
{
/* make sure we don't get passed an external snapshot */
- Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+ Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
@@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot = MemoryContextAllocZero(builder->context, ssize);
- snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+ snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY;
/*
* We misuse the original meaning of SnapshotData's xip and subxip fields
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 251a359bff..4fbad9f0f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
- htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer);
+ htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index b1ae9e5f96..640e9634b3 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -287,7 +287,7 @@ RI_FKey_check(TriggerData *trigdata)
* should be holding pin, but not lock.
*/
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf))
{
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile
index 5a6e6fa4c8..f17b1c5324 100644
--- a/src/backend/utils/time/Makefile
+++ b/src/backend/utils/time/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/time
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = combocid.o tqual.o snapmgr.o
+OBJS = combocid.o snapmgr.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 0b032905a5..b8f195593f 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl;
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY};
+static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY};
+SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
@@ -2046,7 +2046,7 @@ EstimateSnapshotSpace(Snapshot snap)
Size size;
Assert(snap != InvalidSnapshot);
- Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+ Assert(snap->visibility_type == MVCC_VISIBILITY);
/* We allocate any XID arrays needed in the same palloc block. */
size = add_size(sizeof(SerializedSnapshotData),
@@ -2143,7 +2143,7 @@ RestoreSnapshot(char *start_address)
/* Copy all required fields */
snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- snapshot->satisfies = HeapTupleSatisfiesMVCC;
+ snapshot->visibility_type = MVCC_VISIBILITY;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
snapshot->xip = NULL;
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
new file mode 100644
index 0000000000..791f42dd31
--- /dev/null
+++ b/src/include/access/storage_common.h
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_common.h
+ * POSTGRES storage access method definitions shared across
+ * all pluggable storage methods and server.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storage_common.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGE_COMMON_H
+#define STORAGE_COMMON_H
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+
+
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+ HEAPTUPLE_DEAD, /* tuple is dead and deletable */
+ HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
+ HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+ HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
+
+
+/* in storage/storage_common.c */
+extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid);
+extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
+extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+
+/*
+ * SetHintBits()
+ *
+ * Set commit/abort hint bits on a tuple, if appropriate at this time.
+ *
+ * It is only safe to set a transaction-committed hint bit if we know the
+ * transaction's commit record is guaranteed to be flushed to disk before the
+ * buffer, or if the table is temporary or unlogged and will be obliterated by
+ * a crash anyway. We cannot change the LSN of the page here, because we may
+ * hold only a share lock on the buffer, so we can only use the LSN to
+ * interlock this if the buffer's LSN already is newer than the commit LSN;
+ * otherwise we have to just refrain from setting the hint bit until some
+ * future re-examination of the tuple.
+ *
+ * We can always set hint bits when marking a transaction aborted. (Some
+ * code in heapam.c relies on that!)
+ *
+ * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
+ * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
+ * synchronous commits and didn't move tuples that weren't previously
+ * hinted. (This is not known by this subroutine, but is applied by its
+ * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
+ * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
+ * support in-place update from pre-9.0 databases.
+ *
+ * Normal commits may be asynchronous, so for those we need to get the LSN
+ * of the transaction and then check whether this is flushed.
+ *
+ * The caller should pass xid as the XID of the transaction to check, or
+ * InvalidTransactionId if no check is needed.
+ */
+static inline void
+SetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ if (TransactionIdIsValid(xid))
+ {
+ /* NB: xid must be known committed here! */
+ XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
+
+ if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
+ BufferGetLSNAtomic(buffer) < commitLSN)
+ {
+ /* not flushed and no LSN interlock, so don't set hint */
+ return;
+ }
+ }
+
+ tuple->t_infomask |= infomask;
+ MarkBufferDirtyHint(buffer, true);
+}
+
+#endif /* STORAGE_COMMON_H */
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 6fae4eea5c..f5d5edd704 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -11,12 +11,20 @@
#ifndef STORAGEAMAPI_H
#define STORAGEAMAPI_H
+#include "access/storage_common.h"
#include "nodes/nodes.h"
+#include "utils/snapshot.h"
#include "fmgr.h"
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef bool (*SnapshotSatisfies_function) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
+typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, CommandId curcid, Buffer buffer);
+typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+
+
+
/*
* API struct for a storage AM. Note this must be stored in a single palloc'd
* chunk of memory.
@@ -30,6 +38,10 @@ typedef struct StorageAmRoutine
{
NodeTag type;
+ SnapshotSatisfies_function snapshot_satisfies;
+ SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
+ SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+
} StorageAmRoutine;
extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 98b63fc5ba..1a403b8e21 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -20,7 +20,6 @@
#include "storage/relfilenode.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
-#include "utils/tqual.h"
typedef void *Block;
@@ -268,8 +267,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
if (old_snapshot_threshold >= 0
&& (snapshot) != NULL
- && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
- || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+ && ((snapshot)->visibility_type == MVCC_VISIBILITY
+ || (snapshot)->visibility_type == TOAST_VISIBILITY)
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
&& PageGetLSN(page) > (snapshot)->lsn)
TestForOldSnapshot_impl(snapshot, relation);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index bf519778df..3896349b39 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -19,6 +19,18 @@
#include "lib/pairingheap.h"
#include "storage/buf.h"
+typedef enum tuple_visibility_type
+{
+ MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */
+ SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */
+ ANY_VISIBILITY, /* HeapTupleSatisfiesAny */
+ TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */
+ DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */
+ HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */
+ NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */
+
+ END_OF_VISIBILITY
+} tuple_visibility_type;
typedef struct SnapshotData *Snapshot;
@@ -52,7 +64,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
*/
typedef struct SnapshotData
{
- SnapshotSatisfiesFunc satisfies; /* tuple test function */
+ tuple_visibility_type visibility_type; /* tuple visibility test type */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 96eaf01ca0..c2ec33e57c 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,6 +16,7 @@
#define TQUAL_H
#include "utils/snapshot.h"
+#include "access/storageamapi.h"
#include "access/xlogdefs.h"
@@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
- ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
- (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+ ((snapshot)->visibility_type == MVCC_VISIBILITY || \
+ (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY)
/*
* HeapTupleSatisfiesVisibility
@@ -42,47 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
* if so, the indicated buffer is marked dirty.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
- ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
-
-/* Result codes for HeapTupleSatisfiesVacuum */
-typedef enum
-{
- HEAPTUPLE_DEAD, /* tuple is dead and deletable */
- HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
- HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
- HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
-} HTSV_Result;
-
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
- Snapshot snapshot, Buffer buffer);
-
-/* Special "satisfies" routines with different APIs */
-extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
- CommandId curcid, Buffer buffer);
-extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
- TransactionId OldestXmin, Buffer buffer);
-extern bool HeapTupleIsSurelyDead(HeapTuple htup,
- TransactionId OldestXmin);
-extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
-
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \
+ (((method)->snapshot_satisfies) (tuple, snapshot, buffer))
/*
* To avoid leaking too much knowledge about reorderbuffer implementation
@@ -101,14 +63,14 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* local variable of type SnapshotData, and initialize it with this macro.
*/
#define InitDirtySnapshot(snapshotdata) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+ ((snapshotdata).visibility_type = DIRTY_VISIBILITY)
/*
* Similarly, some initialization is required for a NonVacuumable snapshot.
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
*/
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+ ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \
(snapshotdata).xmin = (xmin_horizon))
/*
@@ -116,7 +78,7 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
* to set lsn and whenTaken correctly to support snapshot_too_old.
*/
#define InitToastSnapshot(snapshotdata, l, w) \
- ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+ ((snapshotdata).visibility_type = TOAST_VISIBILITY, \
(snapshotdata).lsn = (l), \
(snapshotdata).whenTaken = (w))
--
2.15.0.windows.1
[application/octet-stream] 0005-slot-hooks-are-added-to-storage-AM.patch (62.7K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/8-0005-slot-hooks-are-added-to-storage-AM.patch)
download | inline diff:
From 477e34008a735e9f340fe4913847d6f6117614c0 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Mon, 4 Dec 2017 14:10:37 +1100
Subject: [PATCH 5/8] slot hooks are added to storage AM
The tuple is removed as part of the slot and added
an void pointer to store the tuple data that can
understand only by the storage AM routine.
The slot utility functions are reorganized to use
two storageAM routines to satify the current
functionality.
Currently the slot supports minimum tuple also.
---
src/backend/access/common/heaptuple.c | 302 +--------------------
src/backend/access/heap/heapam_storage.c | 2 +
src/backend/access/storage/storage_common.c | 404 ++++++++++++++++++++++++++++
src/backend/commands/copy.c | 2 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/trigger.c | 15 +-
src/backend/executor/execExprInterp.c | 35 +--
src/backend/executor/execReplication.c | 90 ++-----
src/backend/executor/execTuples.c | 268 +++++++++---------
src/backend/executor/nodeForeignscan.c | 2 +-
src/backend/executor/nodeModifyTable.c | 24 +-
src/backend/executor/tqueue.c | 2 +-
src/backend/replication/logical/worker.c | 5 +-
src/include/access/htup_details.h | 15 +-
src/include/access/storage_common.h | 38 +++
src/include/access/storageamapi.h | 9 +-
src/include/executor/tuptable.h | 54 ++--
18 files changed, 692 insertions(+), 579 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index a1a9d9905b..c29935b91a 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
#include "postgres.h"
+#include "access/storageamapi.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
@@ -1021,111 +1022,6 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
}
}
-/*
- * slot_deform_tuple
- * Given a TupleTableSlot, extract data from the slot's physical tuple
- * into its Datum/isnull arrays. Data is extracted up through the
- * natts'th column (caller must ensure this is a legal column number).
- *
- * This is essentially an incremental version of heap_deform_tuple:
- * on each call we extract attributes up to the one needed, without
- * re-computing information about previously extracted attributes.
- * slot->tts_nvalid is the number of attributes already extracted.
- */
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- HeapTupleHeader tup = tuple->t_data;
- bool hasnulls = HeapTupleHasNulls(tuple);
- int attnum;
- char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
- bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
- bool slow; /* can we use/set attcacheoff? */
-
- /*
- * Check whether the first call for this tuple, and initialize or restore
- * loop state.
- */
- attnum = slot->tts_nvalid;
- if (attnum == 0)
- {
- /* Start from the first attribute */
- off = 0;
- slow = false;
- }
- else
- {
- /* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
- }
-
- tp = (char *) tup + tup->t_hoff;
-
- for (; attnum < natts; attnum++)
- {
- Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
-
- if (hasnulls && att_isnull(attnum, bp))
- {
- values[attnum] = (Datum) 0;
- isnull[attnum] = true;
- slow = true; /* can't use attcacheoff anymore */
- continue;
- }
-
- isnull[attnum] = false;
-
- if (!slow && thisatt->attcacheoff >= 0)
- off = thisatt->attcacheoff;
- else if (thisatt->attlen == -1)
- {
- /*
- * We can only cache the offset for a varlena attribute if the
- * offset is already suitably aligned, so that there would be no
- * pad bytes in any case: then the offset will be valid for either
- * an aligned or unaligned value.
- */
- if (!slow &&
- off == att_align_nominal(off, thisatt->attalign))
- thisatt->attcacheoff = off;
- else
- {
- off = att_align_pointer(off, thisatt->attalign, -1,
- tp + off);
- slow = true;
- }
- }
- else
- {
- /* not varlena, so safe to use att_align_nominal */
- off = att_align_nominal(off, thisatt->attalign);
-
- if (!slow)
- thisatt->attcacheoff = off;
- }
-
- values[attnum] = fetchatt(thisatt, tp + off);
-
- off = att_addlength_pointer(off, thisatt->attlen, tp + off);
-
- if (thisatt->attlen <= 0)
- slow = true; /* can't use attcacheoff anymore */
- }
-
- /*
- * Save state for next execution
- */
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
/*
* slot_getattr
* This function fetches an attribute of the slot's current tuple.
@@ -1141,91 +1037,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
Datum
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
+ return slot->tts_storageslotam->slot_getattr(slot, attnum, isnull);
}
/*
@@ -1237,40 +1049,7 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
void
slot_getallattrs(TupleTableSlot *slot)
{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, slot->tts_tupleDescriptor->natts);
}
/*
@@ -1281,43 +1060,7 @@ slot_getallattrs(TupleTableSlot *slot)
void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
+ slot->tts_storageslotam->slot_virtualize_tuple(slot, attnum);
}
/*
@@ -1328,42 +1071,11 @@ slot_getsomeattrs(TupleTableSlot *slot, int attnum)
bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
+ bool isnull;
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
+ slot->tts_storageslotam->slot_getattr(slot, attnum, &isnull);
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ return isnull;
}
/*
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 2d3ed44baf..8cccf927e9 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -39,5 +39,7 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate;
amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum;
+ amroutine->slot_storageam = heapam_storage_slot_handler;
+
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/storage/storage_common.c b/src/backend/access/storage/storage_common.c
index 3ede680c89..b65153bb38 100644
--- a/src/backend/access/storage/storage_common.c
+++ b/src/backend/access/storage/storage_common.c
@@ -95,6 +95,410 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
return true;
}
+/*-----------------------
+ *
+ * Slot storage handler API
+ * ----------------------
+ */
+
+static HeapTuple
+heapam_get_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ HeapTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ tup = heap_tuple_from_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_heaptuple;
+ else
+ tup = heap_copytuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+static MinimalTuple
+heapam_get_min_tuple(TupleTableSlot *slot, bool palloc_copy)
+{
+ MinimalTuple tup;
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+
+ if (stuple)
+ {
+ if (stuple->hst_mintuple)
+ {
+ if (!palloc_copy)
+ tup = stuple->hst_mintuple;
+ else
+ tup = heap_copy_minimal_tuple(stuple->hst_mintuple);
+ }
+ else
+ {
+ tup = minimal_tuple_from_heap_tuple(stuple->hst_heaptuple);
+ }
+ }
+ else
+ {
+ tup = heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ return tup;
+}
+
+
+/*
+ * slot_deform_tuple
+ * Given a TupleTableSlot, extract data from the slot's physical tuple
+ * into its Datum/isnull arrays. Data is extracted up through the
+ * natts'th column (caller must ensure this is a legal column number).
+ *
+ * This is essentially an incremental version of heap_deform_tuple:
+ * on each call we extract attributes up to the one needed, without
+ * re-computing information about previously extracted attributes.
+ * slot->tts_nvalid is the number of attributes already extracted.
+ */
+static void
+slot_deform_tuple(TupleTableSlot *slot, int natts)
+{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ Datum *values = slot->tts_values;
+ bool *isnull = slot->tts_isnull;
+ HeapTupleHeader tup = tuple->t_data;
+ bool hasnulls = HeapTupleHasNulls(tuple);
+ int attnum;
+ char *tp; /* ptr to tuple data */
+ long off; /* offset in tuple data */
+ bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
+ bool slow; /* can we use/set attcacheoff? */
+
+ /*
+ * Check whether the first call for this tuple, and initialize or restore
+ * loop state.
+ */
+ attnum = slot->tts_nvalid;
+ if (attnum == 0)
+ {
+ /* Start from the first attribute */
+ off = 0;
+ slow = false;
+ }
+ else
+ {
+ /* Restore state from previous execution */
+ off = stuple->hst_off;
+ slow = stuple->hst_slow;
+ }
+
+ tp = (char *) tup + tup->t_hoff;
+
+ for (; attnum < natts; attnum++)
+ {
+ Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum);
+
+ if (hasnulls && att_isnull(attnum, bp))
+ {
+ values[attnum] = (Datum) 0;
+ isnull[attnum] = true;
+ slow = true; /* can't use attcacheoff anymore */
+ continue;
+ }
+
+ isnull[attnum] = false;
+
+ if (!slow && thisatt->attcacheoff >= 0)
+ off = thisatt->attcacheoff;
+ else if (thisatt->attlen == -1)
+ {
+ /*
+ * We can only cache the offset for a varlena attribute if the
+ * offset is already suitably aligned, so that there would be no
+ * pad bytes in any case: then the offset will be valid for either
+ * an aligned or unaligned value.
+ */
+ if (!slow &&
+ off == att_align_nominal(off, thisatt->attalign))
+ thisatt->attcacheoff = off;
+ else
+ {
+ off = att_align_pointer(off, thisatt->attalign, -1,
+ tp + off);
+ slow = true;
+ }
+ }
+ else
+ {
+ /* not varlena, so safe to use att_align_nominal */
+ off = att_align_nominal(off, thisatt->attalign);
+
+ if (!slow)
+ thisatt->attcacheoff = off;
+ }
+
+ values[attnum] = fetchatt(thisatt, tp + off);
+
+ off = att_addlength_pointer(off, thisatt->attlen, tp + off);
+
+ if (thisatt->attlen <= 0)
+ slow = true; /* can't use attcacheoff anymore */
+ }
+
+ /*
+ * Save state for next execution
+ */
+ slot->tts_nvalid = attnum;
+ stuple->hst_off = off;
+ stuple->hst_slow = slow;
+}
+
+static void
+heapam_slot_virtualize_tuple(TupleTableSlot *slot, int16 upto)
+{
+ HeapamTuple *stuple;
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= upto)
+ return;
+
+ /* Check for caller error */
+ if (upto <= 0 || upto > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", upto);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ stuple = slot->tts_storage; /* XXX SlotGetTupleStorage(slot) ??? */
+ tuple = stuple->hst_heaptuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attno = HeapTupleHeaderGetNatts(tuple->t_data);
+ attno = Min(attno, upto);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < upto; attno++)
+ {
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = upto;
+}
+
+static void
+heapam_slot_update_tuple_tableoid(TupleTableSlot *slot, Oid tableoid)
+{
+ HeapTuple tuple;
+
+ tuple = heapam_get_tuple(slot, false);
+ tuple->t_tableOid = tableoid;
+}
+
+static void
+heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFree, bool minimum_tuple)
+{
+ HeapamTuple *stuple;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ stuple = (HeapamTuple *) palloc0(sizeof(HeapamTuple));
+
+ if (!minimum_tuple)
+ {
+ stuple->hst_heaptuple = tuple;
+ stuple->hst_slow = false;
+ stuple->hst_off = 0;
+ stuple->hst_mintuple = NULL;
+ slot->tts_shouldFreeMin = false;
+ slot->tts_shouldFree = shouldFree;
+ }
+ else
+ {
+ stuple->hst_mintuple = tuple;
+ stuple->hst_minhdr.t_len = ((MinimalTuple) tuple)->t_len + MINIMAL_TUPLE_OFFSET;
+ stuple->hst_minhdr.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
+ stuple->hst_heaptuple = &stuple->hst_minhdr;
+ slot->tts_shouldFreeMin = shouldFree;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ slot->tts_tid = ((HeapTuple) tuple)->t_self;
+ slot->tts_storage = stuple;
+}
+
+static void
+heapam_slot_clear_tuple(TupleTableSlot *slot)
+{
+ HeapamTuple *stuple;
+
+ /* XXX should this be an Assert() instead? */
+ if (slot->tts_isempty)
+ return;
+
+ stuple = slot->tts_storage;
+ if (stuple == NULL)
+ return;
+
+ if (slot->tts_shouldFree)
+ heap_freetuple(stuple->hst_heaptuple);
+
+ if (slot->tts_shouldFreeMin)
+ heap_free_minimal_tuple(stuple->hst_mintuple);
+
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeMin = false;
+
+ pfree(stuple);
+ slot->tts_storage = NULL;
+}
+
+/*
+ * slot_getattr
+ * This function fetches an attribute of the slot's current tuple.
+ * It is functionally equivalent to heap_getattr, but fetches of
+ * multiple attributes of the same tuple will be optimized better,
+ * because we avoid O(N^2) behavior from multiple calls of
+ * nocachegetattr(), even when attcacheoff isn't usable.
+ *
+ * A difference from raw heap_getattr is that attnums beyond the
+ * slot's tupdesc's last attribute will be considered NULL even
+ * when the physical tuple is longer than the tupdesc.
+ */
+static Datum
+heapam_slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ HeapamTuple *stuple = slot->tts_storage;
+ HeapTuple tuple = stuple ? stuple->hst_heaptuple : NULL;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ HeapTupleHeader tup;
+
+ /*
+ * system attributes are handled by heap_getsysattr
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (tuple == &(stuple->hst_minhdr)) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
+ return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+ }
+
+ /*
+ * fast path if desired attribute already cached
+ */
+ if (attnum <= slot->tts_nvalid)
+ {
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should equal
+ * natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance and
+ * table-alteration scenarios: the tuple could be either longer or shorter
+ * than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > HeapTupleHeaderGetNatts(tup))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * check if target attribute is null: no point in groveling through tuple
+ */
+ if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * If the attribute's column has been dropped, we force a NULL result.
+ * This case should not happen in normal use, but it could happen if we
+ * are executing a plan cached before the column was dropped.
+ */
+ if (TupleDescAttr(tupleDesc, (attnum - 1))->attisdropped)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+StorageSlotAmRoutine *
+heapam_storage_slot_handler(void)
+{
+ StorageSlotAmRoutine *amroutine = palloc(sizeof(StorageSlotAmRoutine));
+
+ amroutine->slot_store_tuple = heapam_slot_store_tuple;
+ amroutine->slot_virtualize_tuple = heapam_slot_virtualize_tuple;
+ amroutine->slot_clear_tuple = heapam_slot_clear_tuple;
+ amroutine->slot_tuple = heapam_get_tuple;
+ amroutine->slot_min_tuple = heapam_get_min_tuple;
+ amroutine->slot_getattr = heapam_slot_getattr;
+ amroutine->slot_update_tableoid = heapam_slot_update_tuple_tableoid;
+
+ return amroutine;
+}
/*
* HeapTupleIsSurelyDead
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 254be28ae4..0a0eecf509 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2720,7 +2720,7 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4d77411a68..213a8cccbc 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d2e0376511..b440740e28 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -497,7 +497,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
heap_insert(myState->transientrel,
tuple,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 92ae3822d8..96c4fe7d43 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2289,7 +2289,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2370,7 +2370,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
@@ -2728,7 +2728,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2770,7 +2770,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
if (newSlot != NULL)
{
slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecMaterializeSlot(slot);
+ slottuple = ExecHeapifySlot(slot);
newtuple = slottuple;
}
@@ -2879,7 +2879,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple slottuple = ExecHeapifySlot(slot);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -4006,14 +4006,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
* because we start with a minimal tuple that ExecFetchSlotTuple()
* must materialize anyway.
*/
- LocTriggerData.tg_trigtuple =
- ExecMaterializeSlot(trig_tuple_slot1);
+ LocTriggerData.tg_trigtuple = ExecHeapifySlot(trig_tuple_slot1);
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecMaterializeSlot(trig_tuple_slot2) : NULL;
+ ExecHeapifySlot(trig_tuple_slot2) : NULL;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
break;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 6c4612dad4..42939e2a3b 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -508,13 +508,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ Assert(innerslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(innerslot->tts_storageslotam->slot_is_physical_tuple(innerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(innerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -526,13 +528,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+ Assert(outerslot->tts_storage != NULL);
+ /*
+ * hari
+ * Assert(outerslot->tts_storageslotam->slot_is_physical_tuple(outerslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(outerslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
@@ -544,13 +547,15 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ Assert(scanslot->tts_storage != NULL);
+
+ /*
+ * hari
+ * Assert(scanslot->tts_storageslotam->slot_is_physical_tuple(scanslot));
+ */
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
- op->resnull);
+ d = slot_getattr(scanslot, attnum, op->resnull);
*op->resvalue = d;
EEO_NEXT();
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index bd786a1be6..12c15fd6bc 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -211,59 +211,6 @@ retry:
return found;
}
-/*
- * Compare the tuple and slot and check if they have equal values.
- *
- * We use binary datum comparison which might return false negatives but
- * that's the best we can do here as there may be multiple notions of
- * equality for the data types and table columns don't specify which one
- * to use.
- */
-static bool
-tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
-{
- Datum values[MaxTupleAttributeNumber];
- bool isnull[MaxTupleAttributeNumber];
- int attrnum;
-
- heap_deform_tuple(tup, desc, values, isnull);
-
- /* Check equality of the attributes. */
- for (attrnum = 0; attrnum < desc->natts; attrnum++)
- {
- Form_pg_attribute att;
- TypeCacheEntry *typentry;
-
- /*
- * If one value is NULL and other is not, then they are certainly not
- * equal
- */
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
- return false;
-
- /*
- * If both are NULL, they can be considered equal.
- */
- if (isnull[attrnum])
- continue;
-
- att = TupleDescAttr(desc, attrnum);
-
- typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(att->atttypid))));
-
- if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
- values[attrnum],
- slot->tts_values[attrnum])))
- return false;
- }
-
- return true;
-}
/*
* Search the relation 'rel' for tuple using the sequential scan.
@@ -279,6 +226,7 @@ bool
RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
+ TupleTableSlot *scanslot;
HeapTuple scantuple;
HeapScanDesc scan;
SnapshotData snap;
@@ -292,6 +240,8 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
InitDirtySnapshot(snap);
scan = heap_beginscan(rel, &snap, 0, NULL);
+ scanslot = MakeSingleTupleTableSlot(desc);
+
retry:
found = false;
@@ -300,12 +250,12 @@ retry:
/* Try to find the tuple */
while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (!tuple_equals_slot(desc, scantuple, searchslot))
+ ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
+ if (!ExecSlotCompare(scanslot, searchslot))
continue;
found = true;
- ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
- ExecMaterializeSlot(outslot);
+ ExecCopySlot(outslot, scanslot);
xwait = TransactionIdIsValid(snap.xmin) ?
snap.xmin : snap.xmax;
@@ -329,7 +279,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -362,6 +312,7 @@ retry:
}
heap_endscan(scan);
+ ExecDropSingleTupleTableSlot(scanslot);
return found;
}
@@ -404,7 +355,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can inspect. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, store the tuple and create index entries for it */
simple_heap_insert(rel, tuple);
@@ -442,6 +393,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -453,7 +405,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -469,21 +421,20 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
ExecConstraints(resultRelInfo, slot, estate);
/* Store the slot into tuple that we can write. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, tid, tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+ !HeapTupleIsHeapOnly(tuple))
+ recheckIndexes = ExecInsertIndexTuples(slot, tid,
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -503,6 +454,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
+ ItemPointer tid = &(searchslot->tts_tid);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
@@ -514,7 +466,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ tid,
NULL);
}
@@ -523,11 +475,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, tid);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ tid, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 51d2c5d166..01ca94f9e5 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -82,6 +82,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storage_common.h"
#include "access/tuptoaster.h"
#include "funcapi.h"
#include "catalog/pg_type.h"
@@ -113,16 +114,15 @@ MakeTupleTableSlot(void)
TupleTableSlot *slot = makeNode(TupleTableSlot);
slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->tts_tupleOid = InvalidOid;
+ slot->tts_tableOid = InvalidOid;
+ slot->tts_storageslotam = heapam_storage_slot_handler();
+ slot->tts_storage = NULL;
return slot;
}
@@ -205,6 +205,54 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
return slot;
}
+/* --------------------------------
+ * ExecSlotCompare
+ *
+ * This is a slot comparision function to find out
+ * whether both the slots are same or not?
+ * --------------------------------
+ */
+bool
+ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2)
+{
+ int attrnum;
+
+ Assert(slot1->tts_tupleDescriptor->natts == slot2->tts_tupleDescriptor->natts);
+
+ slot_getallattrs(slot1);
+ slot_getallattrs(slot2);
+
+ /* Check equality of the attributes. */
+ for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
+ {
+ Form_pg_attribute att;
+ TypeCacheEntry *typentry;
+
+ /*
+ * If one value is NULL and other is not, then they are certainly not
+ * equal
+ */
+ if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
+ return false;
+
+ att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
+
+ typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(att->atttypid))));
+
+ if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
+ slot1->tts_values[attrnum],
+ slot2->tts_values[attrnum])))
+ return false;
+ }
+
+ return true;
+}
+
/* --------------------------------
* ExecDropSingleTupleTableSlot
*
@@ -317,7 +365,7 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* --------------------------------
*/
TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
+ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree)
@@ -328,47 +376,27 @@ ExecStoreTuple(HeapTuple tuple,
Assert(tuple != NULL);
Assert(slot != NULL);
Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_storageslotam != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
- * Store the new tuple into the specified slot.
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
*/
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, tuple, shouldFree, false);
+
slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
- {
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
- }
-
return slot;
}
@@ -395,31 +423,19 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
/*
* Free any old physical tuple belonging to the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot); /* XXX ?? */
/*
* Store the new tuple into the specified slot.
*/
slot->tts_isempty = false;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = shouldFree;
- slot->tts_tuple = &slot->tts_minhdr;
- slot->tts_mintuple = mtup;
- slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
- slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
- /* no need to set t_self or t_tableOid since we won't allow access */
+ /*
+ * Store the new tuple into the specified slot, and mark the slot as no
+ * longer empty. This clears any previously stored physical tuple.
+ */
+ /* XXX should we pass the buffer down to the storageAM perhaps? */
+ slot->tts_storageslotam->slot_store_tuple(slot, mtup, false, true);
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -444,25 +460,9 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old physical tuple if necessary.
+ * Tell the storage AM to release any resource associated with the slot.
*/
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
+ slot->tts_storageslotam->slot_clear_tuple(slot);
/*
* Mark it empty.
@@ -541,7 +541,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecCopySlotTuple(TupleTableSlot *slot)
{
/*
@@ -550,20 +550,7 @@ ExecCopySlotTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ return slot->tts_storageslotam->slot_tuple(slot, true);
}
/* --------------------------------
@@ -582,21 +569,19 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ return slot->tts_storageslotam->slot_min_tuple(slot, true);
+}
+void
+ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid)
+{
/*
- * Otherwise we need to build a tuple from the Datum array.
+ * sanity checks
*/
- return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ slot->tts_storageslotam->slot_update_tableoid(slot, tableoid);
}
/* --------------------------------
@@ -614,25 +599,34 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot)
* Hence, the result must be treated as read-only.
* --------------------------------
*/
-HeapTuple
+StorageTuple
ExecFetchSlotTuple(TupleTableSlot *slot)
{
+ MemoryContext oldContext;
+ StorageTuple tup;
+
/*
* sanity checks
*/
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return slot->tts_storageslotam->slot_tuple(slot, false);
/*
- * Otherwise materialize the slot...
+ * Otherwise, copy or build a tuple, and store it into the slot.
+ *
+ * We may be called in a context that is shorter-lived than the tuple
+ * slot, but we have to ensure that the materialized tuple will survive
+ * anyway.
*/
- return ExecMaterializeSlot(slot);
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
+ MemoryContextSwitchTo(oldContext);
+
+ return tup;
}
/* --------------------------------
@@ -652,6 +646,7 @@ MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ MinimalTuple tup;
/*
* sanity checks
@@ -659,11 +654,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a minimal physical tuple (local or not) then just return it.
- */
- if (slot->tts_mintuple)
- return slot->tts_mintuple;
+ if (slot->tts_shouldFreeMin)
+ return slot->tts_storageslotam->slot_min_tuple(slot, false);
/*
* Otherwise, copy or build a minimal tuple, and store it into the slot.
@@ -673,18 +665,11 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_mintuple = ExecCopySlotMinimalTuple(slot);
- slot->tts_shouldFreeMin = true;
+ tup = ExecCopySlotMinimalTuple(slot);
+ ExecStoreMinimalTuple(tup, slot, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Note: we may now have a situation where we have a local minimal tuple
- * attached to a virtual or non-local physical tuple. There seems no harm
- * in that at the moment, but if any materializes, we should change this
- * function to force the slot into minimal-tuple-only state.
- */
-
- return slot->tts_mintuple;
+ return tup;
}
/* --------------------------------
@@ -713,18 +698,19 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
* Force a slot into the "materialized" state.
*
* This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
+ * any external storage.
*
* A typical use for this operation is to prepare a computed tuple
* for being stored on disk. The original data may or may not be
* virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
+ * to scribble on. XXX is this comment good?
* --------------------------------
*/
-HeapTuple
+void
ExecMaterializeSlot(TupleTableSlot *slot)
{
MemoryContext oldContext;
+ HeapTuple tup;
/*
* sanity checks
@@ -732,12 +718,8 @@ ExecMaterializeSlot(TupleTableSlot *slot)
Assert(slot != NULL);
Assert(!slot->tts_isempty);
- /*
- * If we have a regular physical tuple, and it's locally palloc'd, we have
- * nothing to do.
- */
- if (slot->tts_tuple && slot->tts_shouldFree)
- return slot->tts_tuple;
+ if (slot->tts_shouldFree)
+ return;
/*
* Otherwise, copy or build a physical tuple, and store it into the slot.
@@ -747,18 +729,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* anyway.
*/
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
+ tup = ExecCopySlotTuple(slot);
+ ExecStoreTuple(tup, slot, InvalidBuffer, true);
MemoryContextSwitchTo(oldContext);
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
/*
* Mark extracted state invalid. This is important because the slot is
* not supposed to depend any more on the previous external data; we
@@ -768,17 +742,15 @@ ExecMaterializeSlot(TupleTableSlot *slot)
* that we have not pfree'd tts_mintuple, if there is one.)
*/
slot->tts_nvalid = 0;
+}
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
+StorageTuple
+ExecHeapifySlot(TupleTableSlot *slot)
+{
+ ExecMaterializeSlot(slot);
+ Assert(slot->tts_storage != NULL);
- return slot->tts_tuple;
+ return slot->tts_storageslotam->slot_tuple(slot, false);
}
/* --------------------------------
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index dc6cfcfa66..690308845c 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,7 +62,7 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecMaterializeSlot(slot);
+ HeapTuple tup = ExecHeapifySlot(slot);
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 79c34a6e6c..f0307ba50e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -172,7 +172,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
+ tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
econtext->ecxt_outertuple = planSlot;
@@ -272,7 +272,7 @@ ExecInsert(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -407,7 +407,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -420,7 +420,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
newId = InvalidOid;
}
@@ -438,7 +438,7 @@ ExecInsert(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -747,7 +747,7 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
}
else
@@ -898,7 +898,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecMaterializeSlot(rslot);
+ ExecHeapifySlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -959,7 +959,7 @@ ExecUpdate(ModifyTableState *mtstate,
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* get information on the (current) result relation
@@ -978,7 +978,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
@@ -992,7 +992,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* trigger might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
}
else if (resultRelInfo->ri_FdwRoutine)
{
@@ -1008,7 +1008,7 @@ ExecUpdate(ModifyTableState *mtstate,
return NULL;
/* FDW might have changed tuple */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
@@ -1124,7 +1124,7 @@ lreplace:;
{
*tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
goto lreplace;
}
}
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index 0dcb911c3c..ef681e2ec2 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -58,7 +58,7 @@ tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
shm_mq_result result;
/* Send the tuple itself. */
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecHeapifySlot(slot);
result = shm_mq_send(tqueue->queue, tuple->t_len, tuple->t_data, false);
/* Check for failure. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index fa5d9bb120..ad0aceb61e 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -729,9 +729,12 @@ apply_handle_update(StringInfo s)
*/
if (found)
{
+ HeapTuple tuple;
+
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ tuple = ExecHeapifySlot(localslot);
+ ExecStoreTuple(tuple, remoteslot, InvalidBuffer, false);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b0d4c54121..168edb058d 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -20,6 +20,19 @@
#include "access/transam.h"
#include "storage/bufpage.h"
+/*
+ * Opaque tuple representation for executor's TupleTableSlot tts_storage
+ * (XXX This should probably live in a separate header)
+ */
+typedef struct HeapamTuple
+{
+ HeapTuple hst_heaptuple;
+ bool hst_slow;
+ long hst_off;
+ MinimalTuple hst_mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData hst_minhdr; /* workspace for minimal-tuple-only case */
+} HeapamTuple;
+
/*
* MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
* The key limit on this value is that the size of the fixed overhead for
@@ -658,7 +671,7 @@ struct MinimalTupleData
/*
* GETSTRUCT - given a HeapTuple pointer, return address of the user data
*/
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) (((HeapTuple)(TUP))->t_data) + ((HeapTuple)(TUP))->t_data->t_hoff)
/*
* Accessor macros to be used with HeapTuple pointers.
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
index 791f42dd31..5f799406fe 100644
--- a/src/include/access/storage_common.h
+++ b/src/include/access/storage_common.h
@@ -21,9 +21,46 @@
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
+#include "executor/tuptable.h"
#include "storage/bufpage.h"
#include "storage/bufmgr.h"
+/* A physical tuple coming from a storage AM scan */
+typedef void *StorageTuple;
+
+/*
+ * slot storage routine functions
+ */
+typedef void (*SlotStoreTuple_function) (TupleTableSlot *slot,
+ StorageTuple tuple,
+ bool shouldFree,
+ bool minumumtuple);
+typedef void (*SlotClearTuple_function) (TupleTableSlot *slot);
+typedef Datum (*SlotGetattr_function) (TupleTableSlot *slot,
+ int attnum, bool *isnull);
+typedef void (*SlotVirtualizeTuple_function) (TupleTableSlot *slot, int16 upto);
+
+typedef HeapTuple (*SlotGetTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool palloc_copy);
+
+typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid);
+
+typedef void (*SpeculativeAbort_function) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef struct StorageSlotAmRoutine
+{
+ /* Operations on TupleTableSlot */
+ SlotStoreTuple_function slot_store_tuple;
+ SlotVirtualizeTuple_function slot_virtualize_tuple;
+ SlotClearTuple_function slot_clear_tuple;
+ SlotGetattr_function slot_getattr;
+ SlotGetTuple_function slot_tuple;
+ SlotGetMinTuple_function slot_min_tuple;
+ SlotUpdateTableoid_function slot_update_tableoid;
+} StorageSlotAmRoutine;
+
+typedef StorageSlotAmRoutine * (*slot_storageam_hook) (void);
/* Result codes for HeapTupleSatisfiesVacuum */
typedef enum
@@ -42,6 +79,7 @@ extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+extern StorageSlotAmRoutine * heapam_storage_slot_handler(void);
/*
* SetHintBits()
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index f5d5edd704..5e807d827e 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -16,15 +16,14 @@
#include "utils/snapshot.h"
#include "fmgr.h"
-/* A physical tuple coming from a storage AM scan */
-typedef void *StorageTuple;
-
+/*
+ * Storage routine functions
+ */
typedef bool (*SnapshotSatisfies_function) (StorageTuple htup, Snapshot snapshot, Buffer buffer);
typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, CommandId curcid, Buffer buffer);
typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
-
/*
* API struct for a storage AM. Note this must be stored in a single palloc'd
* chunk of memory.
@@ -42,6 +41,8 @@ typedef struct StorageAmRoutine
SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */
SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */
+ slot_storageam_hook slot_storageam;
+
} StorageAmRoutine;
extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index db2a42af5e..e6ff66f14b 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,9 +18,25 @@
#include "access/tupdesc.h"
#include "storage/buf.h"
+/*
+ * Forward declare StorageAmRoutine to avoid including storageamapi.h here
+ */
+struct StorageSlotAmRoutine;
+
+/*
+ * Forward declare StorageTuple to avoid including storageamapi.h here
+ */
+typedef void *StorageTuple;
+
/*----------
* The executor stores tuples in a "tuple table" which is a List of
- * independent TupleTableSlots. There are several cases we need to handle:
+ * independent TupleTableSlots.
+ *
+ * XXX The "html-commented out" text below no longer reflects reality, as
+ * physical tuples are now responsibility of storage AMs. But we have kept
+ * "minimal tuples". Adjust this comment!
+ *
+ * <!-- There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
* 3. "minimal" physical tuple constructed in palloc'ed memory
@@ -56,6 +72,7 @@
* had the fatal defect of invalidating any pass-by-reference Datums pointing
* into the existing slot contents.) Both copies must contain identical data
* payloads when this is the case.
+ * -->
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
@@ -82,11 +99,6 @@
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
*
- * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
- * on the indicated buffer page; drop the pin when we release the
- * slot's reference to that buffer. (tts_shouldFree should always be
- * false in such a case, since presumably tts_tuple is pointing at the
- * buffer page.)
*
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
* arrays. When the slot is holding a "virtual" tuple this must be equal
@@ -114,24 +126,21 @@ typedef struct TupleTableSlot
{
NodeTag type;
bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
+ ItemPointerData tts_tid; /* XXX describe */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
+ Oid tts_tableOid; /* XXX describe */
+ Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
+ uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_shouldFree;
+ bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
- MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
+ struct StorageSlotAmRoutine *tts_storageslotam; /* storage AM */
+ void *tts_storage; /* storage AM's opaque space */
} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
-
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
@@ -143,9 +152,10 @@ extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern bool ExecSlotCompare(TupleTableSlot *slot1, TupleTableSlot *slot2);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
-extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
+extern TupleTableSlot *ExecStoreTuple(void *tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
@@ -155,12 +165,14 @@ extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
-extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern StorageTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
-extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
+extern void ExecSlotUpdateTupleTableoid(TupleTableSlot *slot, Oid tableoid);
+extern StorageTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+extern void ExecMaterializeSlot(TupleTableSlot *slot);
+extern StorageTuple ExecHeapifySlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
--
2.15.0.windows.1
[application/octet-stream] 0006-Tuple-Insert-API-is-added-to-Storage-AM.patch (102.1K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/9-0006-Tuple-Insert-API-is-added-to-Storage-AM.patch)
download | inline diff:
From 4abb8b578a22b3ee46d608af41ec52b50bf072af Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Mon, 11 Dec 2017 21:36:49 +1100
Subject: [PATCH 6/8] Tuple Insert API is added to Storage AM
heap_insert, heap_delete, heap_fetch, heap_update,
heap_get_latest_oid, heap_lock_tuple and heap_multi_insert
functions are added to storage AM. Move the index insertion
logic into storage AM, The Index insert still outside for
the case of multi_insert (Yet to change).
Replaced the usage of HeapTuple with storageTuple in
some places, increased the use of slot.
---
src/backend/access/common/heaptuple.c | 24 +++
src/backend/access/heap/heapam.c | 136 +++++++++-------
src/backend/access/heap/heapam_storage.c | 227 ++++++++++++++++++++++++++
src/backend/access/heap/rewriteheap.c | 5 +-
src/backend/access/heap/tuptoaster.c | 9 +-
src/backend/access/storage/storage_common.c | 16 +-
src/backend/access/storage/storageam.c | 126 ++++++++++++++
src/backend/commands/copy.c | 39 ++---
src/backend/commands/createas.c | 24 +--
src/backend/commands/matview.c | 22 ++-
src/backend/commands/tablecmds.c | 6 +-
src/backend/commands/trigger.c | 50 +++---
src/backend/executor/execIndexing.c | 2 +-
src/backend/executor/execMain.c | 131 ++++++++-------
src/backend/executor/execReplication.c | 72 ++++----
src/backend/executor/nodeLockRows.c | 47 +++---
src/backend/executor/nodeModifyTable.c | 244 +++++++++++++---------------
src/backend/executor/nodeTidscan.c | 23 ++-
src/backend/utils/adt/tid.c | 5 +-
src/include/access/heapam.h | 15 +-
src/include/access/htup_details.h | 1 +
src/include/access/storage_common.h | 5 -
src/include/access/storageam.h | 63 +++++++
src/include/access/storageamapi.h | 102 ++++++++++++
src/include/commands/trigger.h | 2 +-
src/include/executor/executor.h | 15 +-
src/include/executor/tuptable.h | 1 +
src/include/nodes/execnodes.h | 8 +-
28 files changed, 981 insertions(+), 439 deletions(-)
create mode 100644 src/include/access/storageam.h
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c29935b91a..a003df1c28 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
return PointerGetDatum(td);
}
+/*
+ * heap_form_tuple_by_datum
+ * construct a tuple from the given dataum
+ *
+ * The result is allocated in the current memory context.
+ */
+HeapTuple
+heap_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ HeapTuple newTuple;
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td));
+ newTuple->t_len = HeapTupleHeaderGetDatumLength(td);
+ newTuple->t_self = td->t_ctid;
+ newTuple->t_tableOid = tableoid;
+ newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
+ memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len);
+
+ return newTuple;
+}
+
/*
* heap_form_tuple
* construct a tuple from the given values[] and isnull[] arrays,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d63afbaf69..84358748bd 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1893,13 +1893,13 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
*/
bool
heap_fetch(Relation relation,
+ ItemPointer tid,
Snapshot snapshot,
HeapTuple tuple,
Buffer *userbuf,
bool keep_buf,
Relation stats_relation)
{
- ItemPointer tid = &(tuple->t_self);
ItemId lp;
Buffer buffer;
Page page;
@@ -1933,7 +1933,6 @@ heap_fetch(Relation relation,
ReleaseBuffer(buffer);
*userbuf = InvalidBuffer;
}
- tuple->t_data = NULL;
return false;
}
@@ -1955,13 +1954,13 @@ heap_fetch(Relation relation,
ReleaseBuffer(buffer);
*userbuf = InvalidBuffer;
}
- tuple->t_data = NULL;
return false;
}
/*
- * fill in *tuple fields
+ * fill in tuple fields and place it in stuple
*/
+ ItemPointerCopy(tid, &(tuple->t_self));
tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
tuple->t_len = ItemIdGetLength(lp);
tuple->t_tableOid = RelationGetRelid(relation);
@@ -2312,6 +2311,18 @@ heap_get_latest_tid(Relation relation,
} /* end of loop */
}
+/*
+ * HeapTupleSetHintBits --- exported version of SetHintBits()
+ *
+ * This must be separate because of C99's brain-dead notions about how to
+ * implement inline functions.
+ */
+static void
+HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
+ uint16 infomask, TransactionId xid)
+{
+ SetHintBits(tuple, buffer, infomask, xid);
+}
/*
* UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
@@ -4576,13 +4587,12 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update)
* See README.tuplock for a thorough explanation of this mechanism.
*/
HTSU_Result
-heap_lock_tuple(Relation relation, HeapTuple tuple,
+heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
Buffer *buffer, HeapUpdateFailureData *hufd)
{
HTSU_Result result;
- ItemPointer tid = &(tuple->t_self);
ItemId lp;
Page page;
Buffer vmbuffer = InvalidBuffer;
@@ -4595,6 +4605,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
bool first_time = true;
bool have_tuple_lock = false;
bool cleared_all_frozen = false;
+ HeapTupleData tuple;
+
+ Assert(stuple != NULL);
*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
block = ItemPointerGetBlockNumber(tid);
@@ -4614,12 +4627,13 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid));
Assert(ItemIdIsNormal(lp));
- tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple->t_len = ItemIdGetLength(lp);
- tuple->t_tableOid = RelationGetRelid(relation);
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_len = ItemIdGetLength(lp);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ ItemPointerCopy(tid, &tuple.t_self);
l3:
- result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer);
+ result = HeapTupleSatisfiesUpdate(&tuple, cid, *buffer);
if (result == HeapTupleInvisible)
{
@@ -4641,10 +4655,10 @@ l3:
ItemPointerData t_ctid;
/* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
- infomask = tuple->t_data->t_infomask;
- infomask2 = tuple->t_data->t_infomask2;
- ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
+ xwait = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ infomask = tuple.t_data->t_infomask;
+ infomask2 = tuple.t_data->t_infomask2;
+ ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid);
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
@@ -4776,7 +4790,7 @@ l3:
{
HTSU_Result res;
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
GetCurrentTransactionId(),
mode);
if (res != HeapTupleMayBeUpdated)
@@ -4797,8 +4811,8 @@ l3:
* now need to follow the update chain to lock the new
* versions.
*/
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
- ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
+ if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) &&
+ ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
!updated))
goto l3;
@@ -4829,8 +4843,8 @@ l3:
* Make sure it's still an appropriate lock, else start over.
* See above about allowing xmax to change.
*/
- if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask))
+ if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask))
goto l3;
require_sleep = false;
}
@@ -4852,8 +4866,8 @@ l3:
* meantime, start over.
*/
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
xwait))
goto l3;
@@ -4866,9 +4880,9 @@ l3:
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
/* if the xmax changed in the meantime, start over */
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
!TransactionIdEquals(
- HeapTupleHeaderGetRawXmax(tuple->t_data),
+ HeapTupleHeaderGetRawXmax(tuple.t_data),
xwait))
goto l3;
/* otherwise, we're good */
@@ -4893,11 +4907,11 @@ l3:
{
/* ... but if the xmax changed in the meantime, start over */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
xwait))
goto l3;
- Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
+ Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask));
require_sleep = false;
}
@@ -4954,7 +4968,7 @@ l3:
{
case LockWaitBlock:
MultiXactIdWait((MultiXactId) xwait, status, infomask,
- relation, &tuple->t_self, XLTW_Lock, NULL);
+ relation, &tuple.t_self, XLTW_Lock, NULL);
break;
case LockWaitSkip:
if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
@@ -4995,7 +5009,7 @@ l3:
switch (wait_policy)
{
case LockWaitBlock:
- XactLockTableWait(xwait, relation, &tuple->t_self,
+ XactLockTableWait(xwait, relation, &tuple.t_self,
XLTW_Lock);
break;
case LockWaitSkip:
@@ -5022,7 +5036,7 @@ l3:
{
HTSU_Result res;
- res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
+ res = heap_lock_updated_tuple(relation, &tuple, &t_ctid,
GetCurrentTransactionId(),
mode);
if (res != HeapTupleMayBeUpdated)
@@ -5041,8 +5055,8 @@ l3:
* other xact could update this tuple before we get to this point.
* Check for xmax change, and start over if so.
*/
- if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) ||
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data),
xwait))
goto l3;
@@ -5056,7 +5070,7 @@ l3:
* don't check for this in the multixact case, because some
* locker transactions might still be running.
*/
- UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
+ UpdateXmaxHintBits(tuple.t_data, *buffer, xwait);
}
}
@@ -5068,9 +5082,9 @@ l3:
* at all for whatever reason.
*/
if (!require_sleep ||
- (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
+ (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+ HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) ||
+ HeapTupleHeaderIsOnlyLocked(tuple.t_data))
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
@@ -5081,11 +5095,11 @@ failed:
{
Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
result == HeapTupleWouldBlock);
- Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
- hufd->ctid = tuple->t_data->t_ctid;
- hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+ Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID));
+ hufd->ctid = tuple.t_data->t_ctid;
+ hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
if (result == HeapTupleSelfUpdated)
- hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
+ hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data);
else
hufd->cmax = InvalidCommandId;
goto out_locked;
@@ -5108,8 +5122,8 @@ failed:
goto l3;
}
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
- old_infomask = tuple->t_data->t_infomask;
+ xmax = HeapTupleHeaderGetRawXmax(tuple.t_data);
+ old_infomask = tuple.t_data->t_infomask;
/*
* If this is the first possibly-multixact-able operation in the current
@@ -5126,7 +5140,7 @@ failed:
* not modify the tuple just yet, because that would leave it in the wrong
* state if multixact.c elogs.
*/
- compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
+ compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2,
GetCurrentTransactionId(), mode, false,
&xid, &new_infomask, &new_infomask2);
@@ -5142,13 +5156,13 @@ failed:
* Also reset the HOT UPDATE bit, but only if there's no update; otherwise
* we would break the HOT chain.
*/
- tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS;
- tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
- tuple->t_data->t_infomask |= new_infomask;
- tuple->t_data->t_infomask2 |= new_infomask2;
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS;
+ tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
+ tuple.t_data->t_infomask |= new_infomask;
+ tuple.t_data->t_infomask2 |= new_infomask2;
if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
+ HeapTupleHeaderClearHotUpdated(tuple.t_data);
+ HeapTupleHeaderSetXmax(tuple.t_data, xid);
/*
* Make sure there is no forward chain link in t_ctid. Note that in the
@@ -5158,7 +5172,7 @@ failed:
* the tuple as well.
*/
if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
- tuple->t_data->t_ctid = *tid;
+ tuple.t_data->t_ctid = *tid;
/* Clear only the all-frozen bit on visibility map if needed */
if (PageIsAllVisible(page) &&
@@ -5189,10 +5203,10 @@ failed:
XLogBeginInsert();
XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD);
- xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self);
+ xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self);
xlrec.locking_xid = xid;
xlrec.infobits_set = compute_infobits(new_infomask,
- tuple->t_data->t_infomask2);
+ tuple.t_data->t_infomask2);
xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0;
XLogRegisterData((char *) &xlrec, SizeOfHeapLock);
@@ -5226,6 +5240,7 @@ out_unlocked:
if (have_tuple_lock)
UnlockTupleTuplock(relation, tid, mode);
+ *stuple = heap_copytuple(&tuple);
return result;
}
@@ -5683,9 +5698,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
new_infomask = 0;
new_xmax = InvalidTransactionId;
block = ItemPointerGetBlockNumber(&tupid);
- ItemPointerCopy(&tupid, &(mytup.t_self));
- if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
+ if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL))
{
/*
* if we fail to find the updated version of the tuple, it's
@@ -6032,14 +6046,18 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid,
* An explicit confirmation WAL record also makes logical decoding simpler.
*/
void
-heap_finish_speculative(Relation relation, HeapTuple tuple)
+heap_finish_speculative(Relation relation, TupleTableSlot *slot)
{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
Buffer buffer;
Page page;
OffsetNumber offnum;
ItemId lp = NULL;
HeapTupleHeader htup;
+ Assert(slot->tts_speculativeToken != 0);
+
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self)));
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
page = (Page) BufferGetPage(buffer);
@@ -6094,6 +6112,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
END_CRIT_SECTION();
UnlockReleaseBuffer(buffer);
+ slot->tts_speculativeToken = 0;
}
/*
@@ -6123,8 +6142,10 @@ heap_finish_speculative(Relation relation, HeapTuple tuple)
* confirmation records.
*/
void
-heap_abort_speculative(Relation relation, HeapTuple tuple)
+heap_abort_speculative(Relation relation, TupleTableSlot *slot)
{
+ HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage;
+ HeapTuple tuple = stuple->hst_heaptuple;
TransactionId xid = GetCurrentTransactionId();
ItemPointer tid = &(tuple->t_self);
ItemId lp;
@@ -6133,6 +6154,10 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
BlockNumber block;
Buffer buffer;
+ /*
+ * Assert(slot->tts_speculativeToken != 0); This needs some update in
+ * toast
+ */
Assert(ItemPointerIsValid(tid));
block = ItemPointerGetBlockNumber(tid);
@@ -6246,6 +6271,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple)
/* count deletion, as we counted the insertion too */
pgstat_count_heap_delete(relation);
+ slot->tts_speculativeToken = 0;
}
/*
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 8cccf927e9..77fdf7f91a 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -22,6 +22,7 @@
#include "access/storageamapi.h"
#include "utils/builtins.h"
+#include "utils/rel.h"
extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer);
extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
@@ -29,6 +30,219 @@ extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin,
Buffer buffer);
+/* ----------------------------------------------------------------
+ * storage AM support routines for heapam
+ * ----------------------------------------------------------------
+ */
+
+static bool
+heapam_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ HeapTupleData tuple;
+
+ *stuple = NULL;
+ if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation))
+ {
+ *stuple = heap_copytuple(&tuple);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Insert a heap tuple from a slot, which may contain an OID and speculative
+ * insertion token.
+ */
+static Oid
+heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ Oid oid;
+ HeapTuple tuple = NULL;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+
+ if (relation->rd_rel->relhasoids)
+ HeapTupleSetOid(tuple, InvalidOid);
+ }
+ else
+ {
+ /*
+ * Obtain the physical tuple to insert, building from the slot values.
+ * XXX: maybe the slot already contains a physical tuple in the right
+ * format? In fact, if the slot isn't fully deformed, this is
+ * completely bogus ...
+ */
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ /* Set the speculative insertion token, if the slot has one */
+ if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken)
+ HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken);
+
+ /* Perform the insertion, and copy the resulting ItemPointer */
+ oid = heap_insert(relation, tuple, cid, options, bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ if ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0))
+ {
+ Assert(IndexFunc != NULL);
+
+ if (options & HEAP_INSERT_SPECULATIVE)
+ {
+ bool specConflict = false;
+
+ *recheckIndexes = (IndexFunc) (slot, estate, true,
+ &specConflict,
+ arbiterIndexes);
+
+ /* adjust the tuple's state accordingly */
+ if (!specConflict)
+ heap_finish_speculative(relation, slot);
+ else
+ {
+ heap_abort_speculative(relation, slot);
+ slot->tts_specConflict = true;
+ }
+ }
+ else
+ {
+ *recheckIndexes = (IndexFunc) (slot, estate, false,
+ NULL, arbiterIndexes);
+ }
+ }
+
+ return oid;
+}
+
+static HTSU_Result
+heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
+}
+
+
+
+static HTSU_Result
+heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck,
+ bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ HeapTuple tuple;
+ HTSU_Result result;
+
+ if (slot->tts_storage)
+ {
+ HeapamTuple *htuple = slot->tts_storage;
+
+ tuple = htuple->hst_heaptuple;
+ }
+ else
+ {
+ tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+ }
+
+ /* Set the OID, if the slot has one */
+ if (slot->tts_tupleOid != InvalidOid)
+ HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid);
+
+ /* Update the tuple with table oid */
+ if (slot->tts_tableOid != InvalidOid)
+ tuple->t_tableOid = slot->tts_tableOid;
+
+ result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+ hufd, lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
+ if (slot->tts_storage == NULL)
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+
+ /*
+ * Note: instead of having to update the old index tuples associated with
+ * the heap tuple, all we do is form and insert new index tuples. This is
+ * because UPDATEs are actually DELETEs and INSERTs, and index tuple
+ * deletion is done later by VACUUM (see notes in ExecDelete). All we do
+ * here is insert new index tuples. -cim 9/27/89
+ */
+
+ /*
+ * insert index entries for tuple
+ *
+ * Note: heap_update returns the tid (location) of the new tuple in the
+ * t_self field.
+ *
+ * If it's a HOT update, we mustn't insert new index entries.
+ */
+ if ((result == HeapTupleMayBeUpdated) &&
+ ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0)) &&
+ (!HeapTupleIsHeapOnly(tuple)))
+ *recheckIndexes = (IndexFunc) (slot, estate, false, NULL, NIL);
+
+ return result;
+}
+
+static tuple_data
+heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags)
+{
+ switch (flags)
+ {
+ case XMIN:
+ return (tuple_data) HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data);
+ break;
+ case UPDATED_XID:
+ return (tuple_data) HeapTupleHeaderGetUpdateXid(((HeapTuple) tuple)->t_data);
+ break;
+ case CMIN:
+ return (tuple_data) HeapTupleHeaderGetCmin(((HeapTuple) tuple)->t_data);
+ break;
+ case TID:
+ return (tuple_data) ((HeapTuple) tuple)->t_self;
+ break;
+ case CTID:
+ return (tuple_data) ((HeapTuple) tuple)->t_data->t_ctid;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+static StorageTuple
+heapam_form_tuple_by_datum(Datum data, Oid tableoid)
+{
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
Datum
heapam_storage_handler(PG_FUNCTION_ARGS)
{
@@ -41,5 +255,18 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->slot_storageam = heapam_storage_slot_handler;
+ amroutine->tuple_fetch = heapam_fetch;
+ amroutine->tuple_insert = heapam_heap_insert;
+ amroutine->tuple_delete = heapam_heap_delete;
+ amroutine->tuple_update = heapam_heap_update;
+ amroutine->tuple_lock = heap_lock_tuple;
+ amroutine->multi_insert = heap_multi_insert;
+
+ amroutine->get_tuple_data = heapam_get_tuple_data;
+ amroutine->tuple_from_datum = heapam_form_tuple_by_datum;
+ amroutine->tuple_get_latest_tid = heap_get_latest_tid;
+ amroutine->speculative_abort = heap_abort_speculative;
+ amroutine->relation_sync = heap_sync;
+
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f93c194e18..9afca702da 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -110,6 +110,7 @@
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
@@ -126,13 +127,13 @@
#include "storage/bufmgr.h"
#include "storage/fd.h"
+#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
-#include "storage/procarray.h"
/*
* State associated with a rewrite operation. This is opaque to the user
@@ -357,7 +358,7 @@ end_heap_rewrite(RewriteState state)
* wrote before the checkpoint.
*/
if (RelationNeedsWAL(state->rs_new_rel))
- heap_sync(state->rs_new_rel);
+ storage_sync(state->rs_new_rel);
logical_end_heap_rewrite(state);
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a52a..1f67ab2af8 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -32,6 +32,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1777,7 +1778,13 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
* Have a chunk, delete it
*/
if (is_speculative)
- heap_abort_speculative(toastrel, toasttup);
+ {
+ TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel));
+
+ ExecStoreTuple(toasttup, slot, InvalidBuffer, false);
+ storage_abort_speculative(toastrel, slot);
+ ExecDropSingleTupleTableSlot(slot);
+ }
else
simple_heap_delete(toastrel, &toasttup->t_self);
}
diff --git a/src/backend/access/storage/storage_common.c b/src/backend/access/storage/storage_common.c
index b65153bb38..30976f6286 100644
--- a/src/backend/access/storage/storage_common.c
+++ b/src/backend/access/storage/storage_common.c
@@ -29,20 +29,6 @@
SnapshotData SnapshotSelfData = {SELF_VISIBILITY};
SnapshotData SnapshotAnyData = {ANY_VISIBILITY};
-/*
- * HeapTupleSetHintBits --- exported version of SetHintBits()
- *
- * This must be separate because of C99's brain-dead notions about how to
- * implement inline functions.
- */
-void
-HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid)
-{
- SetHintBits(tuple, buffer, infomask, xid);
-}
-
-
/*
* Is the tuple really only locked? That is, is it not updated?
*
@@ -352,6 +338,8 @@ heapam_slot_store_tuple(TupleTableSlot *slot, StorageTuple tuple, bool shouldFre
MemoryContextSwitchTo(oldcontext);
slot->tts_tid = ((HeapTuple) tuple)->t_self;
+ if (slot->tts_tupleDescriptor->tdhasoid)
+ slot->tts_tupleOid = HeapTupleGetOid((HeapTuple) tuple);
slot->tts_storage = stuple;
}
diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c
index 8541c75782..487ffc9746 100644
--- a/src/backend/access/storage/storageam.c
+++ b/src/backend/access/storage/storageam.c
@@ -13,3 +13,129 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+
+#include "access/storageam.h"
+#include "access/storageamapi.h"
+#include "utils/rel.h"
+
+/*
+ * storage_fetch - retrieve tuple with given tid
+ */
+bool
+storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation)
+{
+ return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple,
+ userbuf, keep_buf, stats_relation);
+}
+
+
+/*
+ * storage_lock_tuple - lock a tuple in shared or exclusive mode
+ */
+HTSU_Result
+storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_lock(relation, tid, stuple,
+ cid, mode, wait_policy,
+ follow_updates, buffer, hufd);
+}
+
+/*
+ * Insert a tuple from a slot into storage AM routine
+ */
+Oid
+storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_insert(relation, slot, cid, options,
+ bistate, IndexFunc, estate,
+ arbiterIndexes, recheckIndexes);
+}
+
+/*
+ * Delete a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd)
+{
+ return relation->rd_stamroutine->tuple_delete(relation, tid, cid,
+ crosscheck, wait, hufd);
+}
+
+/*
+ * update a tuple from tid using storage AM routine
+ */
+HTSU_Result
+storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes)
+{
+ return relation->rd_stamroutine->tuple_update(relation, otid, slot, estate,
+ cid, crosscheck, wait, hufd,
+ lockmode, IndexFunc, recheckIndexes);
+}
+
+
+/*
+ * storage_multi_insert - insert multiple tuple into a storage
+ */
+void
+storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate)
+{
+ relation->rd_stamroutine->multi_insert(relation, tuples, ntuples,
+ cid, options, bistate);
+}
+
+/*
+ * storage_abort_speculative - kill a speculatively inserted tuple
+ */
+void
+storage_abort_speculative(Relation relation, TupleTableSlot *slot)
+{
+ relation->rd_stamroutine->speculative_abort(relation, slot);
+}
+
+tuple_data
+storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags)
+{
+ return relation->rd_stamroutine->get_tuple_data(tuple, flags);
+}
+
+StorageTuple
+storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid)
+{
+ if (relation)
+ return relation->rd_stamroutine->tuple_from_datum(data, tableoid);
+ else
+ return heap_form_tuple_by_datum(data, tableoid);
+}
+
+void
+storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid)
+{
+ relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid);
+}
+
+/*
+ * storage_sync - sync a heap, for use when no WAL has been written
+ */
+void
+storage_sync(Relation rel)
+{
+ rel->rd_stamroutine->relation_sync(rel);
+}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0a0eecf509..62d1ac98d5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -20,6 +20,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -2719,8 +2720,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
if (!skip_tuple)
@@ -2783,19 +2782,11 @@ CopyFrom(CopyState cstate)
List *recheckIndexes = NIL;
/* OK, store the tuple and create index entries for it */
- heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid,
- hi_options, bistate);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot,
- &(tuple->t_self),
- estate,
- false,
- NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options,
+ bistate, ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -2891,7 +2882,7 @@ CopyFrom(CopyState cstate)
* indexes since those use WAL anyway)
*/
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(cstate->rel);
+ storage_sync(cstate->rel);
return processed;
}
@@ -2924,12 +2915,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
* before calling it.
*/
oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- heap_multi_insert(cstate->rel,
- bufferedTuples,
- nBufferedTuples,
- mycid,
- hi_options,
- bistate);
+ storage_multi_insert(cstate->rel,
+ bufferedTuples,
+ nBufferedTuples,
+ mycid,
+ hi_options,
+ bistate);
MemoryContextSwitchTo(oldcontext);
/*
@@ -2945,10 +2936,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
recheckIndexes =
- ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
- estate, false, NULL, NIL);
+ ExecInsertIndexTuples(myslot, estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -2965,8 +2955,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 213a8cccbc..9e6fb8740b 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -26,6 +26,7 @@
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -582,25 +583,28 @@ static bool
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_intorel *myState = (DR_intorel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
+ ExecMaterializeSlot(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
*/
if (myState->rel->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
-
- heap_insert(myState->rel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ slot->tts_tupleOid = InvalidOid;
+
+ storage_insert(myState->rel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -619,7 +623,7 @@ intorel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->rel);
+ storage_sync(myState->rel);
/* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b440740e28..936ea9b9e5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -16,6 +16,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -491,19 +492,22 @@ static bool
transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
{
DR_transientrel *myState = (DR_transientrel *) self;
- HeapTuple tuple;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecHeapifySlot(slot);
-
- heap_insert(myState->transientrel,
- tuple,
- myState->output_cid,
- myState->hi_options,
- myState->bistate);
+ ExecMaterializeSlot(slot);
+
+ storage_insert(myState->transientrel,
+ slot,
+ myState->output_cid,
+ myState->hi_options,
+ myState->bistate,
+ NULL,
+ NULL,
+ NIL,
+ NULL);
/* We know this is a newly created relation, so there are no indexes */
@@ -522,7 +526,7 @@ transientrel_shutdown(DestReceiver *self)
/* If we skipped using WAL, must heap_sync before commit */
if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(myState->transientrel);
+ storage_sync(myState->transientrel);
/* close transientrel, but keep lock until commit */
heap_close(myState->transientrel, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d979ce266d..73b68826f2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19,6 +19,7 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/tupconvert.h"
#include "access/xact.h"
@@ -4663,7 +4664,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
- heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ storage_insert(newrel, newslot, mycid, hi_options, bistate,
+ NULL, NULL, NIL, NULL);
ResetExprContext(econtext);
@@ -4687,7 +4689,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* If we skipped writing WAL, then we need to sync the heap. */
if (hi_options & HEAP_INSERT_SKIP_WAL)
- heap_sync(newrel);
+ storage_sync(newrel);
heap_close(newrel, NoLock);
}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 96c4fe7d43..0a58ea16b6 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -15,6 +15,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/htup_details.h"
#include "access/xact.h"
@@ -2352,17 +2353,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
+ {
+ HeapTuple trigtuple = ExecHeapifySlot(slot);
+
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
true, NULL, trigtuple,
recheckIndexes, NULL,
transition_capture);
+ }
}
TupleTableSlot *
@@ -3012,9 +3017,10 @@ GetTupleForTrigger(EState *estate,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
- HeapTupleData tuple;
+ StorageTuple tuple;
HeapTuple result;
Buffer buffer;
+ tuple_data t_data;
if (newSlot != NULL)
{
@@ -3030,11 +3036,11 @@ GetTupleForTrigger(EState *estate,
* lock tuple for update
*/
ltrmark:;
- tuple.t_self = *tid;
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, LockWaitBlock,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, &tuple,
+ estate->es_output_cid,
+ lockmode, LockWaitBlock,
+ false, &buffer, &hufd);
+ result = tuple;
switch (test)
{
case HeapTupleSelfUpdated:
@@ -3066,7 +3072,8 @@ ltrmark:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* it was updated, so look at the updated version */
TupleTableSlot *epqslot;
@@ -3112,6 +3119,7 @@ ltrmark:;
{
Page page;
ItemId lp;
+ HeapTupleData tupledata;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
@@ -3130,17 +3138,17 @@ ltrmark:;
Assert(ItemIdIsNormal(lp));
- tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
- tuple.t_len = ItemIdGetLength(lp);
- tuple.t_self = *tid;
- tuple.t_tableOid = RelationGetRelid(relation);
+ tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tupledata.t_len = ItemIdGetLength(lp);
+ tupledata.t_self = *tid;
+ tupledata.t_tableOid = RelationGetRelid(relation);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+
+ result = heap_copytuple(&tupledata);
}
- result = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
-
return result;
}
@@ -3946,8 +3954,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
AfterTriggerShared evtshared = GetTriggerSharedData(event);
Oid tgoid = evtshared->ats_tgoid;
TriggerData LocTriggerData;
- HeapTupleData tuple1;
- HeapTupleData tuple2;
+ StorageTuple tuple1;
+ StorageTuple tuple2;
HeapTuple rettuple;
Buffer buffer1 = InvalidBuffer;
Buffer buffer2 = InvalidBuffer;
@@ -4020,10 +4028,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
- ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
- LocTriggerData.tg_trigtuple = &tuple1;
+ LocTriggerData.tg_trigtuple = tuple1;
LocTriggerData.tg_trigtuplebuf = buffer1;
}
else
@@ -4037,10 +4044,9 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
- ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
- LocTriggerData.tg_newtuple = &tuple2;
+ LocTriggerData.tg_newtuple = tuple2;
LocTriggerData.tg_newtuplebuf = buffer2;
}
else
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 89e189fa71..ab533cf9c7 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -269,12 +269,12 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
*/
List *
ExecInsertIndexTuples(TupleTableSlot *slot,
- ItemPointer tupleid,
EState *estate,
bool noDupErr,
bool *specConflict,
List *arbiterIndexes)
{
+ ItemPointer tupleid = &slot->tts_tid;
List *result = NIL;
ResultRelInfo *resultRelInfo;
int i;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbaa47f2d3..c3c28d0fac 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -38,6 +38,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -1894,7 +1895,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -1974,7 +1975,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleConversionMap *map;
rel = resultRelInfo->ri_PartitionRoot;
@@ -2021,7 +2022,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
/* See the comment above. */
if (resultRelInfo->ri_PartitionRoot)
{
- HeapTuple tuple = ExecFetchSlotTuple(slot);
+ StorageTuple tuple = ExecFetchSlotTuple(slot);
TupleDesc old_tupdesc = RelationGetDescr(rel);
TupleConversionMap *map;
@@ -2480,7 +2481,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ tuple_data t_data;
Assert(rti > 0);
@@ -2497,7 +2499,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
+
+ t_data = storage_tuple_get_data(relation, copyTuple, TID);
+ *tid = t_data.tid;
/*
* Need to run a recheck subquery. Initialize or reinitialize EPQ state.
@@ -2528,7 +2532,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* is to guard against early re-use of the EPQ query.
*/
if (!TupIsNull(slot))
- (void) ExecMaterializeSlot(slot);
+ ExecMaterializeSlot(slot);
/*
* Clear out the test tuple. This is needed in case the EPQ query is
@@ -2561,14 +2565,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* Note: properly, lockmode should be declared as enum LockTupleMode,
* but we use "int" to avoid having to include heapam.h in executor.h.
*/
-HeapTuple
+StorageTuple
EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
LockWaitPolicy wait_policy,
ItemPointer tid, TransactionId priorXmax)
{
- HeapTuple copyTuple = NULL;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
SnapshotData SnapshotDirty;
+ tuple_data t_data;
/*
* fetch target tuple
@@ -2576,12 +2580,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* Loop here to deal with updated or busy tuples
*/
InitDirtySnapshot(SnapshotDirty);
- tuple.t_self = *tid;
for (;;)
{
Buffer buffer;
+ ItemPointerData ctid;
- if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
+ if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL))
{
HTSU_Result test;
HeapUpdateFailureData hufd;
@@ -2595,7 +2599,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* atomic, and Xmin never changes in an existing tuple, except to
* invalid or frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -2617,7 +2621,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
{
case LockWaitBlock:
XactLockTableWait(SnapshotDirty.xmax,
- relation, &tuple.t_self,
+ relation,
+ tid,
XLTW_FetchUpdated);
break;
case LockWaitSkip:
@@ -2646,20 +2651,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* that priorXmax == xmin, so we can test that variable instead of
* doing HeapTupleHeaderGetXmin again.
*/
- if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
+ if (TransactionIdIsCurrentTransactionId(priorXmax))
{
- ReleaseBuffer(buffer);
- return NULL;
+ t_data = storage_tuple_get_data(relation, tuple, CMIN);
+ if (t_data.cid >= estate->es_output_cid)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
}
/*
* This is a live tuple, so now try to lock it.
*/
- test = heap_lock_tuple(relation, &tuple,
- estate->es_output_cid,
- lockmode, wait_policy,
- false, &buffer, &hufd);
+ test = storage_lock_tuple(relation, tid, tuple,
+ estate->es_output_cid,
+ lockmode, wait_policy,
+ false, &buffer, &hufd);
/* We now have two pins on the buffer, get rid of one */
ReleaseBuffer(buffer);
@@ -2695,12 +2703,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+#if 0 //hari
/* Should not encounter speculative tuple on recheck */
Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data));
- if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+#endif
+ t_data = storage_tuple_get_data(relation, tuple, TID);
+ if (!ItemPointerEquals(&hufd.ctid, &t_data.tid))
{
/* it was updated, so look at the updated version */
- tuple.t_self = hufd.ctid;
+ *tid = hufd.ctid;
/* updated row should have xmin matching this xmax */
priorXmax = hufd.xmax;
continue;
@@ -2722,10 +2733,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
return NULL; /* keep compiler quiet */
}
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
break;
}
@@ -2734,7 +2741,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* If the referenced slot was actually empty, the latest version of
* the row must have been deleted, so we need do nothing.
*/
- if (tuple.t_data == NULL)
+ if (tuple == NULL)
{
ReleaseBuffer(buffer);
return NULL;
@@ -2743,7 +2750,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -2762,7 +2769,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
* As above, it should be safe to examine xmax and t_ctid without the
* buffer content lock, because they can't be changing.
*/
- if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ t_data = storage_tuple_get_data(relation, tuple, CTID);
+ ctid = t_data.tid;
+ if (ItemPointerEquals(tid, &ctid))
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
@@ -2770,17 +2779,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
}
/* updated, so look at the updated row */
- tuple.t_self = tuple.t_data->t_ctid;
+ *tid = ctid;
+
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
+ t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID);
+ priorXmax = t_data.xid;
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
}
/*
- * Return the copied tuple
+ * Return the tuple
*/
- return copyTuple;
+ return tuple;
}
/*
@@ -2826,7 +2837,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
* NB: passed tuple must be palloc'd; it may get freed later
*/
void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple)
{
EState *estate = epqstate->estate;
@@ -2845,7 +2856,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
/*
* Fetch back the current test tuple (if any) for the specified RTI
*/
-HeapTuple
+StorageTuple
EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
{
EState *estate = epqstate->estate;
@@ -2873,7 +2884,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
@@ -2904,8 +2915,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2929,11 +2938,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
- erm,
- datum,
- &updated);
- if (copyTuple == NULL)
+ tuple = fdwroutine->RefetchForeignRow(epqstate->estate,
+ erm,
+ datum,
+ &updated);
+ if (tuple == NULL)
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2947,23 +2956,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* ordinary table, fetch the tuple */
Buffer buffer;
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
ReleaseBuffer(buffer);
}
/* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2973,19 +2977,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid);
+
+ /* store tuple */
+ EvalPlanQualSetTuple(epqstate, erm->rti, tuple);
+
}
}
}
@@ -3154,8 +3151,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
+ estate->es_epqTuple = (StorageTuple *)
+ palloc0(rtsize * sizeof(StorageTuple));
estate->es_epqTupleSet = (bool *)
palloc0(rtsize * sizeof(bool));
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 12c15fd6bc..fcb58a0421 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "commands/trigger.h"
@@ -169,19 +170,19 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+ pfree(locktup);
PopActiveSnapshot();
@@ -277,19 +278,20 @@ retry:
Buffer buf;
HeapUpdateFailureData hufd;
HTSU_Result res;
- HeapTupleData locktup;
-
- ItemPointerCopy(&outslot->tts_tid, &locktup.t_self);
+ StorageTuple locktup;
PushActiveSnapshot(GetLatestSnapshot());
- res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false),
- lockmode,
- LockWaitBlock,
- false /* don't follow updates */ ,
- &buf, &hufd);
+ res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false),
+ lockmode,
+ LockWaitBlock,
+ false /* don't follow updates */ ,
+ &buf, &hufd);
/* the tuple slot already has the buffer pinned */
- ReleaseBuffer(buf);
+ if (BufferIsValid(buf))
+ ReleaseBuffer(buf);
+
+ pfree(locktup);
PopActiveSnapshot();
@@ -327,7 +329,6 @@ void
ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
@@ -354,19 +355,12 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can inspect. */
- tuple = ExecHeapifySlot(slot);
-
- /* OK, store the tuple and create index entries for it */
- simple_heap_insert(rel, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- NIL);
+ storage_insert(resultRelInfo->ri_RelationDesc, slot,
+ GetCurrentCommandId(true), 0, NULL,
+ ExecInsertIndexTuples, estate, NIL, &recheckIndexes);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -390,7 +384,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
TupleTableSlot *searchslot, TupleTableSlot *slot)
{
bool skip_tuple = false;
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = &(searchslot->tts_tid);
@@ -415,22 +409,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
if (!skip_tuple)
{
List *recheckIndexes = NIL;
+ HeapUpdateFailureData hufd;
+ LockTupleMode lockmode;
+ InsertIndexTuples IndexFunc = ExecInsertIndexTuples;
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
- /* Store the slot into tuple that we can write. */
- tuple = ExecHeapifySlot(slot);
+ storage_update(rel, tid, slot, estate, GetCurrentCommandId(true), InvalidSnapshot,
+ true, &hufd, &lockmode, IndexFunc, &recheckIndexes);
- /* OK, update the tuple and index entries for it */
- simple_heap_update(rel, tid, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, tid,
- estate, false, NULL,
- NIL);
+ tuple = ExecHeapifySlot(slot);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..2da5240d24 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -22,6 +22,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
@@ -74,18 +75,20 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
+ StorageTuple *testTuple;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ StorageTuple copyTuple;
+ ItemPointerData tid;
+ tuple_data t_data;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
+ testTuple = (StorageTuple) (&(node->lr_curtuples[erm->rti - 1]));
if (*testTuple != NULL)
heap_freetuple(*testTuple);
*testTuple = NULL;
@@ -159,7 +162,7 @@ lnext:
}
/* okay, try to lock the tuple */
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
+ tid = *((ItemPointer) DatumGetPointer(datum));
switch (erm->markType)
{
case ROW_MARK_EXCLUSIVE:
@@ -180,11 +183,13 @@ lnext:
break;
}
- test = heap_lock_tuple(erm->relation, &tuple,
- estate->es_output_cid,
- lockmode, erm->waitPolicy, true,
- &buffer, &hufd);
- ReleaseBuffer(buffer);
+ test = storage_lock_tuple(erm->relation, &tid, &tuple,
+ estate->es_output_cid,
+ lockmode, erm->waitPolicy, true,
+ &buffer, &hufd);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+
switch (test)
{
case HeapTupleWouldBlock:
@@ -218,7 +223,8 @@ lnext:
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
- if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID);
+ if (ItemPointerEquals(&hufd.ctid, &(t_data.tid)))
{
/* Tuple was deleted, so don't return it */
goto lnext;
@@ -238,7 +244,8 @@ lnext:
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
+ t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID);
+ tid = t_data.tid;
/* Save locked tuple for EvalPlanQual testing below */
*testTuple = copyTuple;
@@ -258,7 +265,7 @@ lnext:
}
/* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
- erm->curCtid = tuple.t_self;
+ erm->curCtid = tid;
}
/*
@@ -280,7 +287,7 @@ lnext:
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTupleData tuple;
+ StorageTuple tuple;
Buffer buffer;
/* skip non-active child tables, but clear their test tuples */
@@ -308,14 +315,12 @@ lnext:
Assert(ItemPointerIsValid(&(erm->curCtid)));
/* okay, fetch the tuple */
- tuple.t_self = erm->curCtid;
- if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
- false, NULL))
+ if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer,
+ false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
+ EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple);
ReleaseBuffer(buffer);
}
@@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Create workspace in which we can remember per-RTE locked tuples
*/
lrstate->lr_ntables = list_length(estate->es_range_table);
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
+ lrstate->lr_curtuples = (StorageTuple *)
+ palloc0(lrstate->lr_ntables * sizeof(StorageTuple));
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index f0307ba50e..86bf30bd06 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,7 +38,10 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
+#include "access/storageam.h"
#include "access/xact.h"
+#include "catalog/pg_am.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
@@ -165,15 +168,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_scantuple = tupleSlot;
else
{
- HeapTuple tuple;
-
/*
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
Assert(!TupIsNull(econtext->ecxt_scantuple));
- tuple = ExecHeapifySlot(econtext->ecxt_scantuple);
- tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple,
+ RelationGetRelid(resultRelInfo->ri_RelationDesc));
}
econtext->ecxt_outertuple = planSlot;
@@ -192,7 +193,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
static void
ExecCheckHeapTupleVisible(EState *estate,
Relation rel,
- HeapTuple tuple,
+ StorageTuple tuple,
Buffer buffer)
{
if (!IsolationUsesXactSnapshot())
@@ -205,13 +206,15 @@ ExecCheckHeapTupleVisible(EState *estate,
LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer))
{
+ tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN);
+
/*
* We should not raise a serialization failure if the conflict is
* against a tuple inserted by our own transaction, even if it's not
* visible to our snapshot. (This would happen, for example, if
* conflicting keys are proposed for insertion in a single command.)
*/
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ if (!TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
@@ -227,19 +230,20 @@ ExecCheckTIDVisible(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid)
{
- Relation rel = relinfo->ri_RelationDesc;
Buffer buffer;
- HeapTupleData tuple;
+ Relation rel = relinfo->ri_RelationDesc;
+ StorageTuple tuple;
/* Redundantly check isolation level */
if (!IsolationUsesXactSnapshot())
return;
- tuple.t_self = *tid;
- if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
+ if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
- ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer);
- ReleaseBuffer(buffer);
+ ExecCheckHeapTupleVisible(estate, rel, tuple, buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
}
/* ----------------------------------------------------------------
@@ -260,7 +264,7 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
@@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate,
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecHeapifySlot(slot);
-
/*
* get information on the (current) result relation
*/
@@ -285,6 +283,8 @@ ExecInsert(ModifyTableState *mtstate,
int leaf_part_index;
TupleConversionMap *map;
+ tuple = ExecHeapifySlot(slot);
+
/*
* Away we go ... If we end up not finding a partition after all,
* ExecFindPartition() does not return and errors out instead.
@@ -375,19 +375,31 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
+ * get the heap tuple out of the tuple table slot, making sure we have a
+ * writable copy <-- obsolete comment XXX explain what we really do here
+ *
+ * Do we really need to do this here?
+ */
+ ExecMaterializeSlot(slot);
+
+
+ /*
+ * If the result relation uses heapam and has OIDs, force the tuple's OID
+ * to zero so that heap_insert will assign a fresh OID. Usually the OID
+ * already will be zero at this point, but there are corner cases where
+ * the plan tree can return a tuple extracted literally from some table
+ * with the same rowtype.
*
* XXX if we ever wanted to allow users to assign their own OIDs to new
* rows, this'd be the place to do it. For the moment, we make a point of
* doing this before calling triggers, so that a user-supplied trigger
* could hack the OID if desired.
*/
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID &&
+ resultRelationDesc->rd_rel->relhasoids)
+ {
+ slot->tts_tupleOid = InvalidOid;
+ }
/*
* BEFORE ROW INSERT Triggers.
@@ -405,9 +417,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
-
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
@@ -419,9 +428,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* trigger might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
newId = InvalidOid;
}
else if (resultRelInfo->ri_FdwRoutine)
@@ -437,14 +443,12 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
newId = InvalidOid;
}
@@ -464,7 +468,8 @@ ExecInsert(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid);
/*
* Check any RLS INSERT WITH CHECK policies
@@ -494,7 +499,6 @@ ExecInsert(ModifyTableState *mtstate,
/* Perform a speculative insertion. */
uint32 specToken;
ItemPointerData conflictTid;
- bool specConflict;
/*
* Do a non-conclusive check for conflicts first.
@@ -509,7 +513,7 @@ ExecInsert(ModifyTableState *mtstate,
* speculatively.
*/
vlock:
- specConflict = false;
+ slot->tts_specConflict = false;
if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
arbiterIndexes))
{
@@ -555,24 +559,17 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete.
*/
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
- HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+ slot->tts_speculativeToken = specToken;
/* insert the tuple, with the speculative token */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- HEAP_INSERT_SPECULATIVE,
- NULL);
-
- /* insert index entries for tuple */
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, true, &specConflict,
- arbiterIndexes);
-
- /* adjust the tuple's state accordingly */
- if (!specConflict)
- heap_finish_speculative(resultRelationDesc, tuple);
- else
- heap_abort_speculative(resultRelationDesc, tuple);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ HEAP_INSERT_SPECULATIVE,
+ NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
/*
* Wake up anyone waiting for our decision. They will re-check
@@ -588,7 +585,7 @@ ExecInsert(ModifyTableState *mtstate,
* the pre-check again, which will now find the conflicting tuple
* (unless it aborts before we get there).
*/
- if (specConflict)
+ if (slot->tts_specConflict)
{
list_free(recheckIndexes);
goto vlock;
@@ -600,19 +597,14 @@ ExecInsert(ModifyTableState *mtstate,
{
/*
* insert the tuple normally.
- *
- * Note: heap_insert returns the tid (location) of the new tuple
- * in the t_self field.
*/
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid,
- 0, NULL);
-
- /* insert index entries for tuple */
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL,
- arbiterIndexes);
+ newId = storage_insert(resultRelationDesc, slot,
+ estate->es_output_cid,
+ 0, NULL,
+ ExecInsertIndexTuples,
+ estate,
+ arbiterIndexes,
+ &recheckIndexes);
}
}
@@ -620,11 +612,11 @@ ExecInsert(ModifyTableState *mtstate,
{
(estate->es_processed)++;
estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
+ setLastTid(&(slot->tts_tid));
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
mtstate->mt_transition_capture);
list_free(recheckIndexes);
@@ -675,7 +667,7 @@ ExecInsert(ModifyTableState *mtstate,
static TupleTableSlot *
ExecDelete(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
@@ -720,8 +712,6 @@ ExecDelete(ModifyTableState *mtstate,
}
else if (resultRelInfo->ri_FdwRoutine)
{
- HeapTuple tuple;
-
/*
* delete from foreign table: let the FDW do it
*
@@ -747,8 +737,10 @@ ExecDelete(ModifyTableState *mtstate,
*/
if (slot->tts_isempty)
ExecStoreAllNullTuple(slot);
- tuple = ExecHeapifySlot(slot);
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+
+ ExecMaterializeSlot(slot);
+
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
}
else
{
@@ -762,11 +754,11 @@ ExecDelete(ModifyTableState *mtstate,
* mode transactions.
*/
ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd);
+ result = storage_delete(resultRelationDesc, tupleid,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -862,7 +854,7 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot.
*/
TupleTableSlot *rslot;
- HeapTupleData deltuple;
+ StorageTuple deltuple = NULL;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine)
@@ -876,20 +868,19 @@ ldelete:;
slot = estate->es_trig_tuple_slot;
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
+ deltuple = heap_copytuple(oldtuple);
delbuffer = InvalidBuffer;
}
else
{
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
+ if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny,
+ &deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
+ ExecStoreTuple(deltuple, slot, InvalidBuffer, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -898,7 +889,7 @@ ldelete:;
* Before releasing the target tuple again, make sure rslot has a
* local copy of any pass-by-reference values.
*/
- ExecHeapifySlot(rslot);
+ ExecMaterializeSlot(rslot);
ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
@@ -935,14 +926,14 @@ ldelete:;
static TupleTableSlot *
ExecUpdate(ModifyTableState *mtstate,
ItemPointer tupleid,
- HeapTuple oldtuple,
+ StorageTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
bool canSetTag)
{
- HeapTuple tuple;
+ StorageTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
HTSU_Result result;
@@ -1007,14 +998,14 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */
return NULL;
- /* FDW might have changed tuple */
- tuple = ExecHeapifySlot(slot);
-
/*
* AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so initialize t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc));
+
+ /* FDW might have changed tuple */
+ tuple = ExecHeapifySlot(slot);
}
else
{
@@ -1024,7 +1015,7 @@ ExecUpdate(ModifyTableState *mtstate,
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
*/
- tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
+ slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
/*
* Check any RLS UPDATE WITH CHECK policies
@@ -1060,11 +1051,14 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot
* mode transactions.
*/
- result = heap_update(resultRelationDesc, tupleid, tuple,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ ,
- &hufd, &lockmode);
+ result = storage_update(resultRelationDesc, tupleid, slot,
+ estate,
+ estate->es_output_cid,
+ estate->es_crosscheck_snapshot,
+ true /* wait for commit */ ,
+ &hufd, &lockmode,
+ ExecInsertIndexTuples,
+ &recheckIndexes);
switch (result)
{
case HeapTupleSelfUpdated:
@@ -1135,26 +1129,6 @@ lreplace:;
elog(ERROR, "unrecognized heap_update status: %u", result);
return NULL;
}
-
- /*
- * Note: instead of having to update the old index tuples associated
- * with the heap tuple, all we do is form and insert new index tuples.
- * This is because UPDATEs are actually DELETEs and INSERTs, and index
- * tuple deletion is done later by VACUUM (see notes in ExecDelete).
- * All we do here is insert new index tuples. -cim 9/27/89
- */
-
- /*
- * insert index entries for tuple
- *
- * Note: heap_update returns the tid (location) of the new tuple in
- * the t_self field.
- *
- * If it's a HOT update, we mustn't insert new index entries.
- */
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false, NULL, NIL);
}
if (canSetTag)
@@ -1212,11 +1186,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
- HeapTupleData tuple;
+ StorageTuple tuple = NULL;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
Buffer buffer;
+ tuple_data t_data;
/* Determine lock mode to use */
lockmode = ExecUpdateLockMode(estate, resultRelInfo);
@@ -1227,10 +1202,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* previous conclusion that the tuple is conclusively committed is not
* true anymore.
*/
- tuple.t_self = *conflictTid;
- test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
- lockmode, LockWaitBlock, false, &buffer,
- &hufd);
+ test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid,
+ lockmode, LockWaitBlock, false, &buffer, &hufd);
switch (test)
{
case HeapTupleMayBeUpdated:
@@ -1255,7 +1228,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* that for SQL MERGE, an exception must be raised in the event of
* an attempt to update the same row twice.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data)))
+ t_data = storage_tuple_get_data(relation, tuple, XMIN);
+ if (TransactionIdIsCurrentTransactionId(t_data.xid))
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
@@ -1286,7 +1260,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* loop here, as the new version of the row might not conflict
* anymore, or the conflicting tuple has actually been deleted.
*/
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return false;
default:
@@ -1314,10 +1290,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
* snapshot. This is in line with the way UPDATE deals with newer tuple
* versions.
*/
- ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer);
+ ExecCheckHeapTupleVisible(estate, relation, tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
+ ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false);
/*
* Make tuple and any needed join variables available to ExecQual and
@@ -1332,7 +1308,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
if (!ExecQual(onConflictSetWhere, econtext))
{
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
InstrCountFiltered1(&mtstate->ps, 1);
return true; /* done with the tuple */
}
@@ -1372,12 +1350,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, conflictTid, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
- ReleaseBuffer(buffer);
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ pfree(tuple);
return true;
}
@@ -1580,7 +1560,7 @@ ExecModifyTable(PlanState *pstate)
ItemPointer tupleid;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
- HeapTuple oldtuple;
+ StorageTuple oldtuple;
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..47d8b7b12f 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -306,7 +307,7 @@ TidNext(TidScanState *node)
ScanDirection direction;
Snapshot snapshot;
Relation heapRelation;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
ItemPointerData *tidList;
@@ -331,12 +332,6 @@ TidNext(TidScanState *node)
tidList = node->tss_TidList;
numTids = node->tss_NumTids;
- /*
- * We use node->tss_htup as the tuple pointer; note this can't just be a
- * local variable here, as the scan tuple slot will keep a pointer to it.
- */
- tuple = &(node->tss_htup);
-
/*
* Initialize or advance scan position, depending on direction.
*/
@@ -364,7 +359,7 @@ TidNext(TidScanState *node)
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
- tuple->t_self = tidList[node->tss_TidPtr];
+ ItemPointerData tid = tidList[node->tss_TidPtr];
/*
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
@@ -372,9 +367,9 @@ TidNext(TidScanState *node)
* current according to our snapshot.
*/
if (node->tss_isCurrentOf)
- heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+ storage_get_latest_tid(heapRelation, snapshot, &tid);
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
+ if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL))
{
/*
* store the scanned tuple in the scan tuple slot of the scan
@@ -385,14 +380,16 @@ TidNext(TidScanState *node)
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
+ InvalidBuffer, /* buffer associated with tuple */
+ true); /* don't pfree */
/*
* At this point we have an extra pin on the buffer, because
* ExecStoreTuple incremented the pin count. Drop our local pin.
*/
- ReleaseBuffer(buffer);
+ /* hari */
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
return slot;
}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 854097dd58..c4481ecee9 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -21,6 +21,7 @@
#include <limits.h>
#include "access/heapam.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -352,7 +353,7 @@ currtid_byreloid(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
@@ -387,7 +388,7 @@ currtid_byrelname(PG_FUNCTION_ARGS)
ItemPointerCopy(tid, result);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- heap_get_latest_tid(rel, snapshot, result);
+ storage_get_latest_tid(rel, snapshot, result);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4e41024e92..251342b01b 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -16,6 +16,7 @@
#include "access/sdir.h"
#include "access/skey.h"
+#include "access/storage_common.h"
#include "nodes/lockoptions.h"
#include "nodes/primnodes.h"
#include "storage/bufpage.h"
@@ -133,7 +134,7 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan);
extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
-extern bool heap_fetch(Relation relation, Snapshot snapshot,
+extern bool heap_fetch(Relation relation, ItemPointer tid, Snapshot snapshot,
HeapTuple tuple, Buffer *userbuf, bool keep_buf,
Relation stats_relation);
extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
@@ -141,7 +142,6 @@ extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
bool *all_dead, bool first_call);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
-
extern void heap_get_latest_tid(Relation relation, Snapshot snapshot,
ItemPointer tid);
extern void setLastTid(const ItemPointer tid);
@@ -157,16 +157,17 @@ extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
CommandId cid, Snapshot crosscheck, bool wait,
HeapUpdateFailureData *hufd);
-extern void heap_finish_speculative(Relation relation, HeapTuple tuple);
-extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
+extern void heap_finish_speculative(Relation relation, TupleTableSlot *slot);
+extern void heap_abort_speculative(Relation relation, TupleTableSlot *slot);
extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
HeapTuple newtup,
CommandId cid, Snapshot crosscheck, bool wait,
HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
-extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
+extern HTSU_Result heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * tuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_update,
Buffer *buffer, HeapUpdateFailureData *hufd);
+
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
TransactionId cutoff_multi);
@@ -182,6 +183,10 @@ extern void simple_heap_update(Relation relation, ItemPointer otid,
extern void heap_sync(Relation relation);
extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+/* in heap/heapam_visibility.c */
+extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid,
+ Buffer buffer);
+
/* in heap/pruneheap.c */
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 168edb058d..489aa78731 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -816,6 +816,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc);
+extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
index 5f799406fe..ff28b4dfec 100644
--- a/src/include/access/storage_common.h
+++ b/src/include/access/storage_common.h
@@ -45,9 +45,6 @@ typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool pal
typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid);
-typedef void (*SpeculativeAbort_function) (Relation rel,
- TupleTableSlot *slot);
-
typedef struct StorageSlotAmRoutine
{
/* Operations on TupleTableSlot */
@@ -74,8 +71,6 @@ typedef enum
/* in storage/storage_common.c */
-extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
- uint16 infomask, TransactionId xid);
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin);
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
new file mode 100644
index 0000000000..e0658a1ddd
--- /dev/null
+++ b/src/include/access/storageam.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * storageam.h
+ * POSTGRES storage access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/storageam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGEAM_H
+#define STORAGEAM_H
+
+#include "access/heapam.h"
+#include "access/storageamapi.h"
+#include "executor/tuptable.h"
+#include "nodes/execnodes.h"
+
+extern bool storage_fetch(Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * stuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
+ CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
+ bool follow_updates,
+ Buffer *buffer, HeapUpdateFailureData *hufd);
+
+extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid,
+ Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd);
+
+extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
+ EState *estate, CommandId cid, Snapshot crosscheck, bool wait,
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc, List **recheckIndexes);
+
+extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+extern void storage_abort_speculative(Relation relation, TupleTableSlot *slot);
+
+extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags);
+
+extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+extern void storage_sync(Relation rel);
+
+#endif
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 5e807d827e..22bc2493ac 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -11,11 +11,33 @@
#ifndef STORAGEAMAPI_H
#define STORAGEAMAPI_H
+#include "access/heapam.h"
#include "access/storage_common.h"
+#include "nodes/execnodes.h"
#include "nodes/nodes.h"
#include "utils/snapshot.h"
#include "fmgr.h"
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+/* Function pointer to let the index tuple insert from storage am */
+typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
+
/*
* Storage routine functions
*/
@@ -24,6 +46,63 @@ typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, Comm
typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer);
+typedef Oid (*TupleInsert_function) (Relation rel, TupleTableSlot *slot, CommandId cid,
+ int options, BulkInsertState bistate, InsertIndexTuples IndexFunc,
+ EState *estate, List *arbiterIndexes, List **recheckIndexes);
+
+typedef HTSU_Result (*TupleDelete_function) (Relation relation,
+ ItemPointer tid,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd);
+
+typedef HTSU_Result (*TupleUpdate_function) (Relation relation,
+ ItemPointer otid,
+ TupleTableSlot *slot,
+ EState *estate,
+ CommandId cid,
+ Snapshot crosscheck,
+ bool wait,
+ HeapUpdateFailureData *hufd,
+ LockTupleMode *lockmode,
+ InsertIndexTuples IndexFunc,
+ List **recheckIndexes);
+
+typedef bool (*TupleFetch_function) (Relation relation,
+ ItemPointer tid,
+ Snapshot snapshot,
+ StorageTuple * tuple,
+ Buffer *userbuf,
+ bool keep_buf,
+ Relation stats_relation);
+
+typedef HTSU_Result (*TupleLock_function) (Relation relation,
+ ItemPointer tid,
+ StorageTuple * tuple,
+ CommandId cid,
+ LockTupleMode mode,
+ LockWaitPolicy wait_policy,
+ bool follow_update,
+ Buffer *buffer,
+ HeapUpdateFailureData *hufd);
+
+typedef void (*MultiInsert_function) (Relation relation, HeapTuple *tuples, int ntuples,
+ CommandId cid, int options, BulkInsertState bistate);
+
+typedef void (*TupleGetLatestTid_function) (Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
+typedef tuple_data(*GetTupleData_function) (StorageTuple tuple, tuple_data_flags flags);
+
+typedef StorageTuple(*TupleFromDatum_function) (Datum data, Oid tableoid);
+
+typedef void (*SpeculativeAbort_function) (Relation rel,
+ TupleTableSlot *slot);
+
+typedef void (*RelationSync_function) (Relation relation);
+
/*
* API struct for a storage AM. Note this must be stored in a single palloc'd
* chunk of memory.
@@ -43,6 +122,29 @@ typedef struct StorageAmRoutine
slot_storageam_hook slot_storageam;
+ /* Operations on physical tuples */
+ TupleInsert_function tuple_insert; /* heap_insert */
+ TupleUpdate_function tuple_update; /* heap_update */
+ TupleDelete_function tuple_delete; /* heap_delete */
+ TupleFetch_function tuple_fetch; /* heap_fetch */
+ TupleLock_function tuple_lock; /* heap_lock_tuple */
+ MultiInsert_function multi_insert; /* heap_multi_insert */
+ TupleGetLatestTid_function tuple_get_latest_tid; /* heap_get_latest_tid */
+
+ GetTupleData_function get_tuple_data;
+ TupleFromDatum_function tuple_from_datum;
+
+ /*
+ * Speculative insertion support operations
+ *
+ * Setting a tuple's speculative token is a slot-only operation, so no
+ * need for a storage AM method, but after inserting a tuple containing a
+ * speculative token, the insertion must be completed by these routines:
+ */
+ SpeculativeAbort_function speculative_abort;
+
+ RelationSync_function relation_sync; /* heap_sync */
+
} StorageAmRoutine;
extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index adbcfa1297..203371148c 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index b5578f5855..fa04a5ea65 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -198,16 +198,16 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, int lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
- int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
- TransactionId priorXmax);
+extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation,
+ int lockmode, LockWaitPolicy wait_policy, ItemPointer tid,
+ TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+ StorageTuple tuple);
+extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
#define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot))
extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
@@ -520,9 +520,8 @@ extern int ExecCleanTargetListLength(List *targetlist);
*/
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative);
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
-extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate, bool noDupErr, bool *specConflict,
- List *arbiterIndexes);
+extern List *ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate,
ItemPointer conflictTid, List *arbiterIndexes);
extern void check_exclusion_constraint(Relation heap, Relation index,
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index e6ff66f14b..7839d5e9b7 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -133,6 +133,7 @@ typedef struct TupleTableSlot
Oid tts_tupleOid; /* XXX describe */
int tts_nvalid; /* # of valid values in tts_values */
uint32 tts_speculativeToken; /* XXX describe */
+ bool tts_specConflict; /* XXX describe */
bool tts_shouldFree;
bool tts_shouldFreeMin;
Datum *tts_values; /* current per-attribute values */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1a35c5c9ad..a956138e4a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -504,7 +504,7 @@ typedef struct EState
* remember if the tuple has been returned already. Arrays are of size
* list_length(es_range_table) and are indexed by scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
+ StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */
bool *es_epqTupleSet; /* true if EPQ tuple is provided */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
@@ -2001,7 +2001,7 @@ typedef struct HashInstrumentation
int nbatch; /* number of batches at end of execution */
int nbatch_original; /* planned number of batches */
size_t space_peak; /* speak memory usage in bytes */
-} HashInstrumentation;
+} HashInstrumentation;
/* ----------------
* Shared memory container for per-worker hash information
@@ -2011,7 +2011,7 @@ typedef struct SharedHashInfo
{
int num_workers;
HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER];
-} SharedHashInfo;
+} SharedHashInfo;
/* ----------------
* HashState information
@@ -2069,7 +2069,7 @@ typedef struct LockRowsState
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
+ StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
--
2.15.0.windows.1
[application/octet-stream] 0007-Scan-functions-are-added-to-storage-AM.patch (105.2K, ../../CAJrrPGdsQSRuq7N3W63CWXVkM_Acgc8HBphDvzOq1ik=Mt6j+Q@mail.gmail.com/10-0007-Scan-functions-are-added-to-storage-AM.patch)
download | inline diff:
From 6df7050db6b88afe01893f7473ed7ff4892fff24 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Fri, 8 Dec 2017 21:53:43 +1100
Subject: [PATCH 7/8] Scan functions are added to storage AM
All the scan functions that are present
in heapam module are moved into heapm_storage
and corresponding function hooks are added.
Replaced HeapTuple with StorageTuple whereever
possible.
Currently directly returning slot functionality
instead of tuple is added only to limited number
of places.
---
contrib/pgrowlocks/pgrowlocks.c | 6 +-
contrib/pgstattuple/pgstattuple.c | 6 +-
src/backend/access/heap/heapam.c | 212 ++++++++++-----------------
src/backend/access/heap/heapam_storage.c | 11 +-
src/backend/access/index/genam.c | 11 +-
src/backend/access/index/indexam.c | 13 +-
src/backend/access/nbtree/nbtinsert.c | 7 +-
src/backend/access/storage/storageam.c | 223 +++++++++++++++++++++++++++++
src/backend/bootstrap/bootstrap.c | 25 ++--
src/backend/catalog/aclchk.c | 13 +-
src/backend/catalog/index.c | 59 ++++----
src/backend/catalog/partition.c | 7 +-
src/backend/catalog/pg_conversion.c | 7 +-
src/backend/catalog/pg_db_role_setting.c | 7 +-
src/backend/catalog/pg_publication.c | 7 +-
src/backend/catalog/pg_subscription.c | 7 +-
src/backend/commands/cluster.c | 13 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 6 +-
src/backend/commands/dbcommands.c | 19 +--
src/backend/commands/indexcmds.c | 7 +-
src/backend/commands/tablecmds.c | 30 ++--
src/backend/commands/tablespace.c | 39 ++---
src/backend/commands/typecmds.c | 13 +-
src/backend/commands/vacuum.c | 13 +-
src/backend/executor/execAmi.c | 2 +-
src/backend/executor/execIndexing.c | 13 +-
src/backend/executor/execReplication.c | 15 +-
src/backend/executor/execTuples.c | 8 +-
src/backend/executor/functions.c | 4 +-
src/backend/executor/nodeAgg.c | 4 +-
src/backend/executor/nodeBitmapHeapscan.c | 19 +--
src/backend/executor/nodeForeignscan.c | 6 +-
src/backend/executor/nodeGather.c | 8 +-
src/backend/executor/nodeGatherMerge.c | 14 +-
src/backend/executor/nodeIndexonlyscan.c | 8 +-
src/backend/executor/nodeIndexscan.c | 16 +--
src/backend/executor/nodeSamplescan.c | 34 ++---
src/backend/executor/nodeSeqscan.c | 45 ++----
src/backend/executor/nodeWindowAgg.c | 4 +-
src/backend/executor/spi.c | 20 +--
src/backend/executor/tqueue.c | 2 +-
src/backend/postmaster/autovacuum.c | 18 +--
src/backend/postmaster/pgstat.c | 7 +-
src/backend/replication/logical/launcher.c | 7 +-
src/backend/rewrite/rewriteDefine.c | 7 +-
src/backend/utils/init/postinit.c | 7 +-
src/include/access/heapam.h | 27 ++--
src/include/access/storage_common.h | 1 +
src/include/access/storageam.h | 58 +++++++-
src/include/access/storageamapi.h | 66 ++++++---
src/include/executor/functions.h | 2 +-
src/include/executor/spi.h | 12 +-
src/include/executor/tqueue.h | 4 +-
src/include/funcapi.h | 2 +-
55 files changed, 735 insertions(+), 469 deletions(-)
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 830e74fd07..bc8b423975 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -125,7 +125,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL);
+ scan = storage_beginscan(rel, GetActiveSnapshot(), 0, NULL);
mydata = palloc(sizeof(*mydata));
mydata->rel = rel;
mydata->scan = scan;
@@ -141,7 +141,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = mydata->scan;
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
HTSU_Result htsu;
TransactionId xmax;
@@ -306,7 +306,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(mydata->rel, AccessShareLock);
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index e098202f84..c4b10d6efc 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -325,13 +325,13 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
StorageAmRoutine *method = rel->rd_stamroutine;
/* Disable syncscan because we assume we scan from block zero upwards */
- scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
+ scan = storage_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false);
InitDirtySnapshot(SnapshotDirty);
nblocks = scan->rs_nblocks; /* # blocks to be scanned */
/* scan the relation */
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -384,7 +384,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
block++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(rel, AccessShareLock);
stat.table_len = (uint64) nblocks * BLCKSZ;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 84358748bd..4565e1c933 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -80,17 +80,6 @@
/* GUC variable */
bool synchronize_seqscans = true;
-
-static HeapScanDesc heap_beginscan_internal(Relation relation,
- Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap);
static void heap_parallelscan_startblock_init(HeapScanDesc scan);
static BlockNumber heap_parallelscan_nextpage(HeapScanDesc scan);
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
@@ -1387,87 +1376,16 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
return r;
}
-
-/* ----------------
- * heap_beginscan - begin relation scan
- *
- * heap_beginscan is the "standard" case.
- *
- * heap_beginscan_catalog differs in setting up its own temporary snapshot.
- *
- * heap_beginscan_strat offers an extended API that lets the caller control
- * whether a nondefault buffer access strategy can be used, and whether
- * syncscan can be chosen (possibly resulting in the scan not starting from
- * block zero). Both of these default to true with plain heap_beginscan.
- *
- * heap_beginscan_bm is an alternative entry point for setting up a
- * HeapScanDesc for a bitmap heap scan. Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality
- * to make it worth using the same data structure.
- *
- * heap_beginscan_sampling is an alternative entry point for setting up a
- * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
- * using the same data structure although the behavior is rather different.
- * In addition to the options offered by heap_beginscan_strat, this call
- * also allows control of whether page-mode visibility checking is used.
- * ----------------
- */
HeapScanDesc
heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
-{
- Oid relid = RelationGetRelid(relation);
- Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
-
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- true, true, true, false, false, true);
-}
-
-HeapScanDesc
-heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, true,
- false, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- false, false, true, true, false, false);
-}
-
-HeapScanDesc
-heap_beginscan_sampling(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
- allow_strat, allow_sync, allow_pagemode,
- false, true, false);
-}
-
-static HeapScanDesc
-heap_beginscan_internal(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- ParallelHeapScanDesc parallel_scan,
- bool allow_strat,
- bool allow_sync,
- bool allow_pagemode,
- bool is_bitmapscan,
- bool is_samplescan,
- bool temp_snap)
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap)
{
HeapScanDesc scan;
@@ -1537,9 +1455,16 @@ heap_beginscan_internal(Relation relation, Snapshot snapshot,
* ----------------
*/
void
-heap_rescan(HeapScanDesc scan,
- ScanKey key)
+heap_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
{
+ if (set_params)
+ {
+ scan->rs_allow_strat = allow_strat;
+ scan->rs_allow_sync = allow_sync;
+ scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+ }
+
/*
* unpin scan buffers
*/
@@ -1550,27 +1475,21 @@ heap_rescan(HeapScanDesc scan,
* reinitialize scan descriptor
*/
initscan(scan, key, true);
-}
-/* ----------------
- * heap_rescan_set_params - restart a relation scan after changing params
- *
- * This call allows changing the buffer strategy, syncscan, and pagemode
- * options before starting a fresh scan. Note that although the actual use
- * of syncscan might change (effectively, enabling or disabling reporting),
- * the previously selected startblock will be kept.
- * ----------------
- */
-void
-heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode)
-{
- /* adjust parameters */
- scan->rs_allow_strat = allow_strat;
- scan->rs_allow_sync = allow_sync;
- scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
- /* ... and rescan */
- heap_rescan(scan, key);
+ /*
+ * reset parallel scan, if present
+ */
+ if (scan->rs_parallel != NULL)
+ {
+ ParallelHeapScanDesc parallel_scan;
+
+ /*
+ * Caller is responsible for making sure that all workers have
+ * finished the scan before calling this.
+ */
+ parallel_scan = scan->rs_parallel;
+ pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
+ }
}
/* ----------------
@@ -1659,25 +1578,6 @@ heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan)
pg_atomic_write_u64(¶llel_scan->phs_nallocated, 0);
}
-/* ----------------
- * heap_beginscan_parallel - join a parallel scan
- *
- * Caller must hold a suitable lock on the correct relation.
- * ----------------
- */
-HeapScanDesc
-heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
-{
- Snapshot snapshot;
-
- Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
- snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
- RegisterSnapshot(snapshot);
-
- return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
- true, true, true, false, false, true);
-}
-
/* ----------------
* heap_parallelscan_startblock_init - find and set the scan's startblock
*
@@ -1822,8 +1722,7 @@ heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
#define HEAPDEBUG_3
#endif /* !defined(HEAPDEBUGALL) */
-
-HeapTuple
+StorageTuple
heap_getnext(HeapScanDesc scan, ScanDirection direction)
{
/* Note: no locking manipulations needed */
@@ -1853,6 +1752,53 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction)
return heap_copytuple(&(scan->rs_ctup));
}
+#ifdef HEAPAMSLOTDEBUGALL
+#define HEAPAMSLOTDEBUG_1 \
+ elog(DEBUG2, "heapam_getnext([%s,nkeys=%d],dir=%d) called", \
+ RelationGetRelationName(scan->rs_rd), scan->rs_nkeys, (int) direction)
+#define HEAPAMSLOTDEBUG_2 \
+ elog(DEBUG2, "heapam_getnext returning EOS")
+#define HEAPAMSLOTDEBUG_3 \
+ elog(DEBUG2, "heapam_getnext returning tuple")
+#else
+#define HEAPAMSLOTDEBUG_1
+#define HEAPAMSLOTDEBUG_2
+#define HEAPAMSLOTDEBUG_3
+#endif
+
+TupleTableSlot *
+heap_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ HeapScanDesc scan = (HeapScanDesc) sscan;
+
+ /* Note: no locking manipulations needed */
+
+ HEAPAMSLOTDEBUG_1; /* heap_getnext( info ) */
+
+ if (scan->rs_pageatatime)
+ heapgettup_pagemode(scan, direction,
+ scan->rs_nkeys, scan->rs_key);
+ else
+ heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
+
+ if (scan->rs_ctup.t_data == NULL)
+ {
+ HEAPAMSLOTDEBUG_2; /* heap_getnext returning EOS */
+ ExecClearTuple(slot);
+ return slot;
+ }
+
+ /*
+ * if we get here it means we have a new current scan tuple, so point to
+ * the proper return buffer and return the tuple.
+ */
+ HEAPAMSLOTDEBUG_3; /* heap_getnext returning tuple */
+
+ pgstat_count_heap_getnext(scan->rs_rd);
+ return ExecStoreTuple(heap_copytuple(&(scan->rs_ctup)),
+ slot, InvalidBuffer, true);
+}
+
/*
* heap_fetch - retrieve tuple with given tid
*
diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c
index 77fdf7f91a..4ee3def639 100644
--- a/src/backend/access/heap/heapam_storage.c
+++ b/src/backend/access/heap/heapam_storage.c
@@ -148,8 +148,6 @@ heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid,
return heap_delete(relation, tid, cid, crosscheck, wait, hufd);
}
-
-
static HTSU_Result
heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
EState *estate, CommandId cid, Snapshot crosscheck,
@@ -255,6 +253,15 @@ heapam_storage_handler(PG_FUNCTION_ARGS)
amroutine->slot_storageam = heapam_storage_slot_handler;
+ amroutine->scan_begin = heap_beginscan;
+ amroutine->scansetlimits = heap_setscanlimits;
+ amroutine->scan_getnext = heap_getnext;
+ amroutine->scan_getnextslot = heap_getnextslot;
+ amroutine->scan_end = heap_endscan;
+ amroutine->scan_rescan = heap_rescan;
+ amroutine->scan_update_snapshot = heap_update_snapshot;
+ amroutine->hot_search_buffer = heap_hot_search_buffer;
+
amroutine->tuple_fetch = heapam_fetch;
amroutine->tuple_insert = heapam_heap_insert;
amroutine->tuple_delete = heapam_heap_delete;
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 01321a2543..26a9ccb657 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "catalog/index.h"
#include "lib/stringinfo.h"
@@ -394,9 +395,9 @@ systable_beginscan(Relation heapRelation,
* disadvantage; and there are no compensating advantages, because
* it's unlikely that such scans will occur in parallel.
*/
- sysscan->scan = heap_beginscan_strat(heapRelation, snapshot,
- nkeys, key,
- true, false);
+ sysscan->scan = storage_beginscan_strat(heapRelation, snapshot,
+ nkeys, key,
+ true, false);
sysscan->iscan = NULL;
}
@@ -432,7 +433,7 @@ systable_getnext(SysScanDesc sysscan)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else
- htup = heap_getnext(sysscan->scan, ForwardScanDirection);
+ htup = storage_getnext(sysscan->scan, ForwardScanDirection);
return htup;
}
@@ -504,7 +505,7 @@ systable_endscan(SysScanDesc sysscan)
index_close(sysscan->irel, AccessShareLock);
}
else
- heap_endscan(sysscan->scan);
+ storage_endscan(sysscan->scan);
if (sysscan->snapshot)
UnregisterSnapshot(sysscan->snapshot);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index edf4172eb2..0afc00625b 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -71,6 +71,7 @@
#include "access/amapi.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
@@ -605,12 +606,12 @@ index_fetch_heap(IndexScanDesc scan)
/* Obtain share-lock on the buffer so we can examine visibility */
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
- scan->xs_cbuf,
- scan->xs_snapshot,
- &scan->xs_ctup,
- &all_dead,
- !scan->xs_continue_hot);
+ got_heap_tuple = storage_hot_search_buffer(tid, scan->heapRelation,
+ scan->xs_cbuf,
+ scan->xs_snapshot,
+ &scan->xs_ctup,
+ &all_dead,
+ !scan->xs_continue_hot);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
if (got_heap_tuple)
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 310589da4e..0368a10dbc 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
@@ -325,8 +326,8 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* that satisfies SnapshotDirty. This is necessary because we
* have just a single index entry for the entire chain.
*/
- else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
- &all_dead))
+ else if (storage_hot_search(&htid, heapRel, &SnapshotDirty,
+ &all_dead))
{
TransactionId xwait;
@@ -379,7 +380,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
- if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+ if (storage_hot_search(&htid, heapRel, SnapshotSelf, NULL))
{
/* Normal case --- it's still live */
}
diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c
index 487ffc9746..c326a54f70 100644
--- a/src/backend/access/storage/storageam.c
+++ b/src/backend/access/storage/storageam.c
@@ -16,7 +16,9 @@
#include "access/storageam.h"
#include "access/storageamapi.h"
+#include "access/relscan.h"
#include "utils/rel.h"
+#include "utils/tqual.h"
/*
* storage_fetch - retrieve tuple with given tid
@@ -48,6 +50,174 @@ storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
follow_updates, buffer, hufd);
}
+/* ----------------
+ * heap_beginscan_parallel - join a parallel scan
+ *
+ * Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+ Snapshot snapshot;
+
+ Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+ snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+ RegisterSnapshot(snapshot);
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, 0, NULL, parallel_scan,
+ true, true, true, false, false, true);
+}
+
+/*
+ * heap_setscanlimits - restrict range of a heapscan
+ *
+ * startBlk is the page to start at
+ * numBlks is number of pages to scan (InvalidBlockNumber means "all")
+ */
+void
+storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
+{
+ sscan->rs_rd->rd_stamroutine->scansetlimits(sscan, startBlk, numBlks);
+}
+
+
+/* ----------------
+ * heap_beginscan - begin relation scan
+ *
+ * heap_beginscan is the "standard" case.
+ *
+ * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+ *
+ * heap_beginscan_strat offers an extended API that lets the caller control
+ * whether a nondefault buffer access strategy can be used, and whether
+ * syncscan can be chosen (possibly resulting in the scan not starting from
+ * block zero). Both of these default to true with plain heap_beginscan.
+ *
+ * heap_beginscan_bm is an alternative entry point for setting up a
+ * HeapScanDesc for a bitmap heap scan. Although that scan technology is
+ * really quite unlike a standard seqscan, there is just enough commonality
+ * to make it worth using the same data structure.
+ *
+ * heap_beginscan_sampling is an alternative entry point for setting up a
+ * HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
+ * using the same data structure although the behavior is rather different.
+ * In addition to the options offered by heap_beginscan_strat, this call
+ * also allows control of whether page-mode visibility checking is used.
+ * ----------------
+ */
+HeapScanDesc
+storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ true, true, true, false, false, true);
+}
+
+HeapScanDesc
+storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, true,
+ false, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ false, false, true, true, false, false);
+}
+
+HeapScanDesc
+storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ return relation->rd_stamroutine->scan_begin(relation, snapshot, nkeys, key, NULL,
+ allow_strat, allow_sync, allow_pagemode,
+ false, true, false);
+}
+
+/* ----------------
+ * heap_rescan - restart a relation scan
+ * ----------------
+ */
+void
+storage_rescan(HeapScanDesc scan,
+ ScanKey key)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, false, false, false, false);
+}
+
+/* ----------------
+ * heap_rescan_set_params - restart a relation scan after changing params
+ *
+ * This call allows changing the buffer strategy, syncscan, and pagemode
+ * options before starting a fresh scan. Note that although the actual use
+ * of syncscan might change (effectively, enabling or disabling reporting),
+ * the previously selected startblock will be kept.
+ * ----------------
+ */
+void
+storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode)
+{
+ scan->rs_rd->rd_stamroutine->scan_rescan(scan, key, true,
+ allow_strat, allow_sync, (allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot)));
+}
+
+/* ----------------
+ * heap_endscan - end relation scan
+ *
+ * See how to integrate with index scans.
+ * Check handling if reldesc caching.
+ * ----------------
+ */
+void
+storage_endscan(HeapScanDesc scan)
+{
+ scan->rs_rd->rd_stamroutine->scan_end(scan);
+}
+
+
+/* ----------------
+ * heap_update_snapshot
+ *
+ * Update snapshot info in heap scan descriptor.
+ * ----------------
+ */
+void
+storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot)
+{
+ scan->rs_rd->rd_stamroutine->scan_update_snapshot(scan, snapshot);
+}
+
+StorageTuple
+storage_getnext(HeapScanDesc sscan, ScanDirection direction)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnext(sscan, direction);
+}
+
+TupleTableSlot *
+storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
+{
+ return sscan->rs_rd->rd_stamroutine->scan_getnextslot(sscan, direction, slot);
+}
+
/*
* Insert a tuple from a slot into storage AM routine
*/
@@ -87,6 +257,59 @@ storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
lockmode, IndexFunc, recheckIndexes);
}
+/*
+ * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot
+ *
+ * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
+ * of a HOT chain), and buffer is the buffer holding this tuple. We search
+ * for the first chain member satisfying the given snapshot. If one is
+ * found, we update *tid to reference that tuple's offset number, and
+ * return true. If no match, return false without modifying *tid.
+ *
+ * heapTuple is a caller-supplied buffer. When a match is found, we return
+ * the tuple here, in addition to updating *tid. If no match is found, the
+ * contents of this buffer on return are undefined.
+ *
+ * If all_dead is not NULL, we check non-visible tuples to see if they are
+ * globally dead; *all_dead is set true if all members of the HOT chain
+ * are vacuumable, false if not.
+ *
+ * Unlike heap_fetch, the caller must already have pin and (at least) share
+ * lock on the buffer; it is still pinned/locked at exit. Also unlike
+ * heap_fetch, we do not report any pgstats count; caller may do so if wanted.
+ */
+bool
+storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call)
+{
+ return relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, heapTuple, all_dead, first_call);
+}
+
+/*
+ * heap_hot_search - search HOT chain for tuple satisfying snapshot
+ *
+ * This has the same API as heap_hot_search_buffer, except that the caller
+ * does not provide the buffer containing the page, rather we access it
+ * locally.
+ */
+bool
+storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead)
+{
+ bool result;
+ Buffer buffer;
+ HeapTupleData heapTuple;
+
+ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ result = relation->rd_stamroutine->hot_search_buffer(tid, relation, buffer,
+ snapshot, &heapTuple, all_dead, true);
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ ReleaseBuffer(buffer);
+ return result;
+}
/*
* storage_multi_insert - insert multiple tuple into a storage
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 8287de97a2..a73a363a49 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -18,6 +18,7 @@
#include <signal.h>
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "bootstrap/bootstrap.h"
@@ -586,18 +587,18 @@ boot_openrel(char *relname)
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memcpy((char *) &(*app)->am_typ,
@@ -605,7 +606,7 @@ boot_openrel(char *relname)
sizeof((*app)->am_typ));
app++;
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
}
@@ -916,25 +917,25 @@ gettype(char *type)
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
i = 0;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
++i;
- heap_endscan(scan);
+ storage_endscan(scan);
app = Typ = ALLOC(struct typmap *, i + 1);
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
app = Typ;
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
(*app)->am_oid = HeapTupleGetOid(tup);
memmove((char *) &(*app++)->am_typ,
(char *) GETSTRUCT(tup),
sizeof((*app)->am_typ));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, NoLock);
return gettype(type);
}
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index e481cf3d11..0b3be57a88 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -20,6 +20,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
@@ -847,14 +848,14 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
InvalidOid);
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, keycount, key);
+ scan = storage_beginscan_catalog(rel, keycount, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
break;
@@ -892,14 +893,14 @@ getRelationsInNamespace(Oid namespaceId, char relkind)
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 2, key);
+ scan = storage_beginscan_catalog(rel, 2, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return relations;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index ead8d2abdf..16bb70ef45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
@@ -1907,10 +1908,10 @@ index_update_stats(Relation rel,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
- tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
+ pg_class_scan = storage_beginscan_catalog(pg_class, 1, key);
+ tuple = storage_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
- heap_endscan(pg_class_scan);
+ storage_endscan(pg_class_scan);
}
else
{
@@ -2282,16 +2283,16 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
method = heapRelation->rd_stamroutine;
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- allow_sync); /* syncscan OK? */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ allow_sync); /* syncscan OK? */
/* set our scan endpoints */
if (!allow_sync)
- heap_setscanlimits(scan, start_blockno, numblocks);
+ storage_setscanlimits(scan, start_blockno, numblocks);
else
{
/* syncscan can only be requested on whole relation */
@@ -2304,7 +2305,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/*
* Scan all tuples in the base relation.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
bool tupleIsAlive;
@@ -2616,7 +2617,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
/* we can now forget our snapshot, if set */
if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
@@ -2687,14 +2688,14 @@ IndexCheckExclusion(Relation heapRelation,
* Scan all live tuples in the base relation.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- true); /* syncscan OK */
-
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ true); /* syncscan OK */
+
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2730,7 +2731,7 @@ IndexCheckExclusion(Relation heapRelation,
estate, true);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
@@ -3007,17 +3008,17 @@ validate_index_heapscan(Relation heapRelation,
* here, because it's critical that we read from block zero forward to
* match the sorted TIDs.
*/
- scan = heap_beginscan_strat(heapRelation, /* relation */
- snapshot, /* snapshot */
- 0, /* number of keys */
- NULL, /* scan key */
- true, /* buffer access strategy OK */
- false); /* syncscan not OK */
+ scan = storage_beginscan_strat(heapRelation, /* relation */
+ snapshot, /* snapshot */
+ 0, /* number of keys */
+ NULL, /* scan key */
+ true, /* buffer access strategy OK */
+ false); /* syncscan not OK */
/*
* Scan all tuples matching the snapshot.
*/
- while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((heapTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ItemPointer heapcursor = &heapTuple->t_self;
ItemPointerData rootTuple;
@@ -3174,7 +3175,7 @@ validate_index_heapscan(Relation heapRelation,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index ef156e449e..d6f1a9cad5 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -19,6 +19,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -1323,7 +1324,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(part_rel, snapshot, 0, NULL);
+ scan = storage_beginscan(part_rel, snapshot, 0, NULL);
tupslot = MakeSingleTupleTableSlot(tupdesc);
/*
@@ -1332,7 +1333,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, tupslot, InvalidBuffer, false);
econtext->ecxt_scantuple = tupslot;
@@ -1348,7 +1349,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(tupslot);
FreeExecutorState(estate);
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 5746dc349a..1d048e6394 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -161,14 +162,14 @@ RemoveConversionById(Oid conversionOid)
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
+ scan = storage_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
- if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ if (HeapTupleIsValid(tuple = storage_getnext(scan, ForwardScanDirection)))
CatalogTupleDelete(rel, &tuple->t_self);
else
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 323471bc83..517e3101cd 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_db_role_setting.h"
@@ -196,12 +197,12 @@ DropSetting(Oid databaseid, Oid roleid)
numkeys++;
}
- scan = heap_beginscan_catalog(relsetting, numkeys, keys);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(relsetting, numkeys, keys);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(relsetting, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relsetting, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 3ef7ba8cd5..145e3c1d65 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -21,6 +21,7 @@
#include "access/hash.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -324,9 +325,9 @@ GetAllTablesPublicationRelations(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_RELATION));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relid = HeapTupleGetOid(tuple);
Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -335,7 +336,7 @@ GetAllTablesPublicationRelations(void)
result = lappend_oid(result, relid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index fb53d71cd6..a51f2e4dfc 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/indexing.h"
@@ -402,12 +403,12 @@ RemoveSubscriptionRel(Oid subid, Oid relid)
}
/* Do the search and delete what we found. */
- scan = heap_beginscan_catalog(rel, nkeys, skey);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ scan = storage_beginscan_catalog(rel, nkeys, skey);
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
CatalogTupleDelete(rel, &tup->t_self);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dbcc5bc172..e0f6973a3f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -21,6 +21,7 @@
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
+#include "access/storageam.h"
#include "access/storageamapi.h"
#include "access/transam.h"
#include "access/tuptoaster.h"
@@ -909,7 +910,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
+ heapScan = storage_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
indexScan = NULL;
}
@@ -959,7 +960,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
}
else
{
- tuple = heap_getnext(heapScan, ForwardScanDirection);
+ tuple = storage_getnext(heapScan, ForwardScanDirection);
if (tuple == NULL)
break;
@@ -1045,7 +1046,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
if (indexScan != NULL)
index_endscan(indexScan);
if (heapScan != NULL)
- heap_endscan(heapScan);
+ storage_endscan(heapScan);
/*
* In scan-and-sort mode, complete the sort, then read out all live tuples
@@ -1656,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan_catalog(indRelation, 1, &entry);
- while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(indRelation, 1, &entry);
+ while ((indexTuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -1677,7 +1678,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
MemoryContextSwitchTo(old_context);
}
- heap_endscan(scan);
+ storage_endscan(scan);
relation_close(indRelation, AccessShareLock);
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e2544e51ed..6727d154e1 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "access/storageam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
@@ -102,7 +103,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
* removed.
*/
tmptid = new_row->t_self;
- if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
+ if (!storage_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
{
/*
* All rows in the HOT chain are dead, so skip the check.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 62d1ac98d5..f2209d9f81 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2038,10 +2038,10 @@ CopyTo(CopyState cstate)
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
- scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
+ scandesc = storage_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
processed = 0;
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scandesc, ForwardScanDirection)) != NULL)
{
CHECK_FOR_INTERRUPTS();
@@ -2053,7 +2053,7 @@ CopyTo(CopyState cstate)
processed++;
}
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
pfree(values);
pfree(nulls);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index eb1a4695c0..87d651002e 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -26,6 +26,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
@@ -590,8 +591,8 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
Oid dsttablespace;
@@ -643,7 +644,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
XLOG_DBASE_CREATE | XLR_SPECIAL_REL_UPDATE);
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
/*
@@ -1875,8 +1876,8 @@ remove_dbtablespaces(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1917,7 +1918,7 @@ remove_dbtablespaces(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
}
@@ -1942,8 +1943,8 @@ check_db_file_conflict(Oid db_id)
HeapTuple tuple;
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
char *dstpath;
@@ -1966,7 +1967,7 @@ check_db_file_conflict(Oid db_id)
pfree(dstpath);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 97091dd9fb..400f936a2f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -18,6 +18,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -1964,8 +1965,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
* rels will be processed indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relationRelation, num_keys, scan_keys);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(relationRelation, num_keys, scan_keys);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
Oid relid = HeapTupleGetOid(tuple);
@@ -2005,7 +2006,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
MemoryContextSwitchTo(old);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relationRelation, AccessShareLock);
/* Now reindex each rel in a separate transaction */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 73b68826f2..3d4fa831b3 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4551,7 +4551,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* checking all the constraints.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(oldrel, snapshot, 0, NULL);
+ scan = storage_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -4559,7 +4559,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
*/
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (tab->rewrite > 0)
{
@@ -4673,7 +4673,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
MemoryContextSwitchTo(oldCxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
@@ -5076,9 +5076,9 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan_catalog(classRel, 1, key);
+ scan = storage_beginscan_catalog(classRel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
if (behavior == DROP_RESTRICT)
ereport(ERROR,
@@ -5090,7 +5090,7 @@ find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior be
result = lappend_oid(result, HeapTupleGetOid(tuple));
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(classRel, AccessShareLock);
return result;
@@ -8260,7 +8260,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
@@ -8268,7 +8268,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
*/
oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -8283,7 +8283,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
}
MemoryContextSwitchTo(oldcxt);
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
@@ -8338,9 +8338,9 @@ validateForeignKeyConstraint(char *conname,
* ereport(ERROR) and that's that.
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
@@ -8369,7 +8369,7 @@ validateForeignKeyConstraint(char *conname,
RI_FKey_check_ins(&fcinfo);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
}
@@ -10874,8 +10874,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
ObjectIdGetDatum(orig_tablespaceoid));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 1, key);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan_catalog(rel, 1, key);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid relOid = HeapTupleGetOid(tuple);
Form_pg_class relForm;
@@ -10934,7 +10934,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
relations = lappend_oid(relations, relOid);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
if (relations == NIL)
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index d574e4dd00..6804182db7 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -53,6 +53,7 @@
#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -416,8 +417,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
{
@@ -434,7 +435,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
(errmsg("tablespace \"%s\" does not exist, skipping",
tablespacename)));
/* XXX I assume I need one or both of these next two calls */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
}
return;
@@ -461,7 +462,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
*/
CatalogTupleDelete(rel, &tuple->t_self);
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
/*
* Remove any comments or security labels on this tablespace.
@@ -925,8 +926,8 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -937,7 +938,7 @@ RenameTableSpace(const char *oldname, const char *newname)
newtuple = heap_copytuple(tup);
newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
- heap_endscan(scan);
+ storage_endscan(scan);
/* Must be owner */
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
@@ -955,15 +956,15 @@ RenameTableSpace(const char *oldname, const char *newname)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scan, ForwardScanDirection);
+ scan = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("tablespace \"%s\" already exists",
newname)));
- heap_endscan(scan);
+ storage_endscan(scan);
/* OK, update the entry */
namestrcpy(&(newform->spcname), newname);
@@ -1005,8 +1006,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tup = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tup = storage_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1047,7 +1048,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
heap_freetuple(newtuple);
/* Conclude heap scan. */
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, NoLock);
return tablespaceoid;
@@ -1396,8 +1397,8 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1405,7 +1406,7 @@ get_tablespace_oid(const char *tablespacename, bool missing_ok)
else
result = InvalidOid;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
if (!OidIsValid(result) && !missing_ok)
@@ -1442,8 +1443,8 @@ get_tablespace_name(Oid spc_oid)
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan_catalog(rel, 1, entry);
- tuple = heap_getnext(scandesc, ForwardScanDirection);
+ scandesc = storage_beginscan_catalog(rel, 1, entry);
+ tuple = storage_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(tuple))
@@ -1451,7 +1452,7 @@ get_tablespace_name(Oid spc_oid)
else
result = NULL;
- heap_endscan(scandesc);
+ storage_endscan(scandesc);
heap_close(rel, AccessShareLock);
return result;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f86af4c054..8a339d16be 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -32,6 +32,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
@@ -2387,8 +2388,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2417,7 +2418,7 @@ AlterDomainNotNull(List *names, bool notNull)
}
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
@@ -2783,8 +2784,8 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
/* Scan all tuples in this relation */
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(testrel, snapshot, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(testrel, snapshot, 0, NULL);
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
@@ -2827,7 +2828,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
ResetExprContext(econtext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 4abe6b15e0..3a8dc8eb0c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -28,6 +28,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/namespace.h"
@@ -533,9 +534,9 @@ get_all_vacuum_rels(void)
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pgclass, 0, NULL);
+ scan = storage_beginscan_catalog(pgclass, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
MemoryContext oldcontext;
@@ -562,7 +563,7 @@ get_all_vacuum_rels(void)
MemoryContextSwitchTo(oldcontext);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pgclass, AccessShareLock);
return vacrels;
@@ -1213,9 +1214,9 @@ vac_truncate_clog(TransactionId frozenXID,
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(relation, 0, NULL);
+ scan = storage_beginscan_catalog(relation, 0, NULL);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
TransactionId datfrozenxid = dbform->datfrozenxid;
@@ -1252,7 +1253,7 @@ vac_truncate_clog(TransactionId frozenXID,
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(relation, AccessShareLock);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index f1636a5b88..dde843174e 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -544,7 +544,7 @@ static bool
IndexSupportsBackwardScan(Oid indexid)
{
bool result;
- HeapTuple ht_idxrel;
+ StorageTuple ht_idxrel;
Form_pg_class idxrelrec;
IndexAmRoutine *amroutine;
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index ab533cf9c7..e852718100 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -650,7 +650,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
Oid *index_collations = index->rd_indcollation;
int index_natts = index->rd_index->indnatts;
IndexScanDesc index_scan;
- HeapTuple tup;
+ StorageTuple tup;
ScanKeyData scankeys[INDEX_MAX_KEYS];
SnapshotData DirtySnapshot;
int i;
@@ -732,12 +732,13 @@ retry:
bool existing_isnull[INDEX_MAX_KEYS];
char *error_new;
char *error_existing;
+ tuple_data t_data = storage_tuple_get_data(heap, tup, TID);
/*
* Ignore the entry for the tuple we're trying to check.
*/
if (ItemPointerIsValid(tupleid) &&
- ItemPointerEquals(tupleid, &tup->t_self))
+ ItemPointerEquals(tupleid, &(t_data.tid)))
{
if (found_self) /* should not happen */
elog(ERROR, "found self tuple multiple times in index \"%s\"",
@@ -785,7 +786,8 @@ retry:
DirtySnapshot.speculativeToken &&
TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
{
- ctid_wait = tup->t_data->t_ctid;
+ t_data = storage_tuple_get_data(heap, tup, CTID);
+ ctid_wait = t_data.tid;
reason_wait = indexInfo->ii_ExclusionOps ?
XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
index_endscan(index_scan);
@@ -805,7 +807,10 @@ retry:
{
conflict = true;
if (conflictTid)
- *conflictTid = tup->t_self;
+ {
+ t_data = storage_tuple_get_data(heap, tup, TID);
+ *conflictTid = t_data.tid;
+ }
break;
}
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fcb58a0421..d5492fd260 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -118,7 +118,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
TupleTableSlot *searchslot,
TupleTableSlot *outslot)
{
- HeapTuple scantuple;
+ StorageTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
@@ -228,8 +228,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot)
{
TupleTableSlot *scanslot;
- HeapTuple scantuple;
- HeapScanDesc scan;
+ StorageScanDesc scan;
SnapshotData snap;
TransactionId xwait;
bool found;
@@ -239,19 +238,19 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start a heap scan. */
InitDirtySnapshot(snap);
- scan = heap_beginscan(rel, &snap, 0, NULL);
+ scan = storage_beginscan(rel, &snap, 0, NULL);
scanslot = MakeSingleTupleTableSlot(desc);
retry:
found = false;
- heap_rescan(scan, NULL);
+ storage_rescan(scan, NULL);
/* Try to find the tuple */
- while ((scantuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while ((scanslot = storage_getnextslot(scan, ForwardScanDirection, scanslot))
+ && !TupIsNull(scanslot))
{
- ExecStoreTuple(scantuple, scanslot, InvalidBuffer, false);
if (!ExecSlotCompare(scanslot, searchslot))
continue;
@@ -313,7 +312,7 @@ retry:
}
}
- heap_endscan(scan);
+ storage_endscan(scan);
ExecDropSingleTupleTableSlot(scanslot);
return found;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 01ca94f9e5..e9a042c02c 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -682,7 +682,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
Datum
ExecFetchSlotTupleDatum(TupleTableSlot *slot)
{
- HeapTuple tup;
+ StorageTuple tup;
TupleDesc tupdesc;
/* Fetch slot's contents in regular-physical-tuple form */
@@ -766,7 +766,7 @@ ExecHeapifySlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
+ StorageTuple newTuple;
MemoryContext oldContext;
/*
@@ -1086,7 +1086,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
* values is an array of C strings, one for each attribute of the return tuple.
* A NULL string pointer indicates we want to create a NULL field.
*/
-HeapTuple
+StorageTuple
BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
{
TupleDesc tupdesc = attinmeta->tupdesc;
@@ -1094,7 +1094,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Datum *dvalues;
bool *nulls;
int i;
- HeapTuple tuple;
+ StorageTuple tuple;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 3caa343723..a8048c3884 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -181,7 +181,7 @@ static void sqlfunction_destroy(DestReceiver *self);
* polymorphic arguments.
*/
SQLFunctionParseInfoPtr
-prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation)
{
@@ -598,7 +598,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
MemoryContext fcontext;
MemoryContext oldcontext;
Oid rettype;
- HeapTuple procedureTuple;
+ StorageTuple procedureTuple;
Form_pg_proc procedureStruct;
SQLFunctionCachePtr fcache;
List *raw_parsetree_list;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index da6ef1a94c..ae4b4769ca 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -3130,7 +3130,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
int numDirectArgs;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
AclResult aclresult;
Oid transfn_oid,
@@ -3255,7 +3255,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 2c5c95d425..0c5ae8fa41 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -38,6 +38,7 @@
#include <math.h>
#include "access/relscan.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h"
@@ -433,8 +434,8 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
- if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
- &heapTuple, NULL, true))
+ if (storage_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+ &heapTuple, NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
@@ -747,7 +748,7 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
PlanState *outerPlan = outerPlanState(node);
/* rescan to release any page pin */
- heap_rescan(node->ss.ss_currentScanDesc, NULL);
+ storage_rescan(node->ss.ss_currentScanDesc, NULL);
/* release bitmaps and buffers if any */
if (node->tbmiterator)
@@ -837,7 +838,7 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
/*
* close heap scan
*/
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -952,10 +953,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* Even though we aren't going to do a conventional seqscan, it is useful
* to create a HeapScanDesc --- most of the fields in it are usable.
*/
- scanstate->ss.ss_currentScanDesc = heap_beginscan_bm(currentRelation,
- estate->es_snapshot,
- 0,
- NULL);
+ scanstate->ss.ss_currentScanDesc = storage_beginscan_bm(currentRelation,
+ estate->es_snapshot,
+ 0,
+ NULL);
/*
* get the scan type from the relation descriptor.
@@ -1123,5 +1124,5 @@ ExecBitmapHeapInitializeWorker(BitmapHeapScanState *node,
node->pstate = pstate;
snapshot = RestoreSnapshot(pstate->phs_snapshot_data);
- heap_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
+ storage_update_snapshot(node->ss.ss_currentScanDesc, snapshot);
}
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 690308845c..353dcdc9ca 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -62,9 +62,9 @@ ForeignNext(ForeignScanState *node)
*/
if (plan->fsSystemCol && !TupIsNull(slot))
{
- HeapTuple tup = ExecHeapifySlot(slot);
-
- tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+ ExecMaterializeSlot(slot);
+ ExecSlotUpdateTupleTableoid(slot,
+ RelationGetRelid(node->ss.ss_currentRelation));
}
return slot;
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index a44cf8409a..aabcc03013 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -46,7 +46,7 @@
static TupleTableSlot *ExecGather(PlanState *pstate);
static TupleTableSlot *gather_getnext(GatherState *gatherstate);
-static HeapTuple gather_readnext(GatherState *gatherstate);
+static StorageTuple gather_readnext(GatherState *gatherstate);
static void ExecShutdownGatherWorkers(GatherState *node);
@@ -254,7 +254,7 @@ gather_getnext(GatherState *gatherstate)
PlanState *outerPlan = outerPlanState(gatherstate);
TupleTableSlot *outerTupleSlot;
TupleTableSlot *fslot = gatherstate->funnel_slot;
- HeapTuple tup;
+ StorageTuple tup;
while (gatherstate->nreaders > 0 || gatherstate->need_to_scan_locally)
{
@@ -292,7 +292,7 @@ gather_getnext(GatherState *gatherstate)
/*
* Attempt to read a tuple from one of our parallel workers.
*/
-static HeapTuple
+static StorageTuple
gather_readnext(GatherState *gatherstate)
{
int nvisited = 0;
@@ -300,7 +300,7 @@ gather_readnext(GatherState *gatherstate)
for (;;)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
bool readerdone;
/* Check for async events, particularly messages from workers. */
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 4a8a59eabf..470ac1c3a0 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -45,7 +45,7 @@
*/
typedef struct GMReaderTupleBuffer
{
- HeapTuple *tuple; /* array of length MAX_TUPLE_STORE */
+ StorageTuple *tuple; /* array of length MAX_TUPLE_STORE */
int nTuples; /* number of tuples currently stored */
int readCounter; /* index of next tuple to extract */
bool done; /* true if reader is known exhausted */
@@ -54,8 +54,8 @@ typedef struct GMReaderTupleBuffer
static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
static int32 heap_compare_slots(Datum a, Datum b, void *arg);
static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
-static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
- bool nowait, bool *done);
+static StorageTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
+ bool nowait, bool *done);
static void ExecShutdownGatherMergeWorkers(GatherMergeState *node);
static void gather_merge_setup(GatherMergeState *gm_state);
static void gather_merge_init(GatherMergeState *gm_state);
@@ -407,7 +407,7 @@ gather_merge_setup(GatherMergeState *gm_state)
{
/* Allocate the tuple array with length MAX_TUPLE_STORE */
gm_state->gm_tuple_buffers[i].tuple =
- (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE);
+ (StorageTuple *) palloc0(sizeof(StorageTuple) * MAX_TUPLE_STORE);
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state);
@@ -625,7 +625,7 @@ static bool
gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
{
GMReaderTupleBuffer *tuple_buffer;
- HeapTuple tup;
+ StorageTuple tup;
/*
* If we're being asked to generate a tuple from the leader, then we just
@@ -696,12 +696,12 @@ gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait)
/*
* Attempt to read a tuple from given worker.
*/
-static HeapTuple
+static StorageTuple
gm_readnext_tuple(GatherMergeState *gm_state, int nreader, bool nowait,
bool *done)
{
TupleQueueReader *reader;
- HeapTuple tup;
+ StorageTuple tup;
/* Check for async events, particularly messages from workers. */
CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index c54c5aa659..e00c9bdbfc 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -117,7 +117,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
- HeapTuple tuple = NULL;
+ StorageTuple tuple = NULL;
CHECK_FOR_INTERRUPTS();
@@ -186,9 +186,9 @@ IndexOnlyNext(IndexOnlyScanState *node)
/*
* Fill the scan tuple slot with data from the index. This might be
- * provided in either HeapTuple or IndexTuple format. Conceivably an
- * index AM might fill both fields, in which case we prefer the heap
- * format, since it's probably a bit cheaper to fill a slot from.
+ * provided in either StorageTuple or IndexTuple format. Conceivably
+ * an index AM might fill both fields, in which case we prefer the
+ * heap format, since it's probably a bit cheaper to fill a slot from.
*/
if (scandesc->xs_hitup)
{
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 2ffef23107..60780e4a99 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -51,7 +51,7 @@
typedef struct
{
pairingheap_node ph_node;
- HeapTuple htup;
+ StorageTuple htup;
Datum *orderbyvals;
bool *orderbynulls;
} ReorderTuple;
@@ -65,9 +65,9 @@ static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
IndexScanState *node);
static int reorderqueue_cmp(const pairingheap_node *a,
const pairingheap_node *b, void *arg);
-static void reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+static void reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls);
-static HeapTuple reorderqueue_pop(IndexScanState *node);
+static StorageTuple reorderqueue_pop(IndexScanState *node);
/* ----------------------------------------------------------------
@@ -84,7 +84,7 @@ IndexNext(IndexScanState *node)
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -185,7 +185,7 @@ IndexNextWithReorder(IndexScanState *node)
EState *estate;
ExprContext *econtext;
IndexScanDesc scandesc;
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
ReorderTuple *topmost = NULL;
bool was_exact;
@@ -483,7 +483,7 @@ reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
* Helper function to push a tuple to the reorder queue.
*/
static void
-reorderqueue_push(IndexScanState *node, HeapTuple tuple,
+reorderqueue_push(IndexScanState *node, StorageTuple tuple,
Datum *orderbyvals, bool *orderbynulls)
{
IndexScanDesc scandesc = node->iss_ScanDesc;
@@ -516,10 +516,10 @@ reorderqueue_push(IndexScanState *node, HeapTuple tuple,
/*
* Helper function to pop the next tuple from the reorder queue.
*/
-static HeapTuple
+static StorageTuple
reorderqueue_pop(IndexScanState *node)
{
- HeapTuple result;
+ StorageTuple result;
ReorderTuple *topmost;
int i;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 6a118d1883..7e990dc35e 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -29,10 +29,12 @@
static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
static TupleTableSlot *SampleNext(SampleScanState *node);
static void tablesample_init(SampleScanState *scanstate);
-static HeapTuple tablesample_getnext(SampleScanState *scanstate);
-static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
+static StorageTuple tablesample_getnext(SampleScanState *scanstate);
+static bool SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset,
HeapScanDesc scan);
+/* hari */
+
/* ----------------------------------------------------------------
* Scan Support
* ----------------------------------------------------------------
@@ -47,7 +49,7 @@ static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
static TupleTableSlot *
SampleNext(SampleScanState *node)
{
- HeapTuple tuple;
+ StorageTuple tuple;
TupleTableSlot *slot;
/*
@@ -244,7 +246,7 @@ ExecEndSampleScan(SampleScanState *node)
* close heap scan
*/
if (node->ss.ss_currentScanDesc)
- heap_endscan(node->ss.ss_currentScanDesc);
+ storage_endscan(node->ss.ss_currentScanDesc);
/*
* close the heap relation.
@@ -349,19 +351,19 @@ tablesample_init(SampleScanState *scanstate)
if (scanstate->ss.ss_currentScanDesc == NULL)
{
scanstate->ss.ss_currentScanDesc =
- heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
- scanstate->ss.ps.state->es_snapshot,
- 0, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_beginscan_sampling(scanstate->ss.ss_currentRelation,
+ scanstate->ss.ps.state->es_snapshot,
+ 0, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
else
{
- heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
- scanstate->use_bulkread,
- allow_sync,
- scanstate->use_pagemode);
+ storage_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
+ scanstate->use_bulkread,
+ allow_sync,
+ scanstate->use_pagemode);
}
pfree(params);
@@ -376,7 +378,7 @@ tablesample_init(SampleScanState *scanstate)
* Note: an awful lot of this is copied-and-pasted from heapam.c. It would
* perhaps be better to refactor to share more code.
*/
-static HeapTuple
+static StorageTuple
tablesample_getnext(SampleScanState *scanstate)
{
TsmRoutine *tsm = scanstate->tsmroutine;
@@ -554,7 +556,7 @@ tablesample_getnext(SampleScanState *scanstate)
* Check visibility of the tuple.
*/
static bool
-SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
+SampleTupleVisible(StorageTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) //hari
{
if (scan->rs_pageatatime)
{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index a5bd60e579..7cfc2107f6 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -28,6 +28,7 @@
#include "postgres.h"
#include "access/relscan.h"
+#include "access/storageam.h"
#include "executor/execdebug.h"
#include "executor/nodeSeqscan.h"
#include "utils/rel.h"
@@ -49,8 +50,7 @@ static TupleTableSlot *SeqNext(SeqScanState *node);
static TupleTableSlot *
SeqNext(SeqScanState *node)
{
- HeapTuple tuple;
- HeapScanDesc scandesc;
+ StorageScanDesc scandesc;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
@@ -69,35 +69,16 @@ SeqNext(SeqScanState *node)
* We reach here if the scan is not parallel, or if we're executing a
* scan that was intended to be parallel serially.
*/
- scandesc = heap_beginscan(node->ss.ss_currentRelation,
- estate->es_snapshot,
- 0, NULL);
+ scandesc = storage_beginscan(node->ss.ss_currentRelation,
+ estate->es_snapshot,
+ 0, NULL);
node->ss.ss_currentScanDesc = scandesc;
}
/*
* get the next tuple from the table
*/
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * 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)
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with this
- * tuple */
- false); /* don't pfree this pointer */
- else
- ExecClearTuple(slot);
-
- return slot;
+ return storage_getnextslot(scandesc, direction, slot);
}
/*
@@ -225,7 +206,7 @@ void
ExecEndSeqScan(SeqScanState *node)
{
Relation relation;
- HeapScanDesc scanDesc;
+ StorageScanDesc scanDesc;
/*
* get information from node
@@ -248,7 +229,7 @@ ExecEndSeqScan(SeqScanState *node)
* close heap scan
*/
if (scanDesc != NULL)
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
/*
* close the heap relation.
@@ -270,13 +251,13 @@ ExecEndSeqScan(SeqScanState *node)
void
ExecReScanSeqScan(SeqScanState *node)
{
- HeapScanDesc scan;
+ StorageScanDesc scan;
scan = node->ss.ss_currentScanDesc;
if (scan != NULL)
- heap_rescan(scan, /* scan desc */
- NULL); /* new scan keys */
+ storage_rescan(scan, /* scan desc */
+ NULL); /* new scan keys */
ExecScanReScan((ScanState *) node);
}
@@ -323,7 +304,7 @@ ExecSeqScanInitializeDSM(SeqScanState *node,
estate->es_snapshot);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
/* ----------------------------------------------------------------
@@ -355,5 +336,5 @@ ExecSeqScanInitializeWorker(SeqScanState *node,
pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
node->ss.ss_currentScanDesc =
- heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+ storage_beginscan_parallel(node->ss.ss_currentRelation, pscan);
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 02868749f6..a68584378b 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2092,7 +2092,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
{
Oid inputTypes[FUNC_MAX_ARGS];
int numArguments;
- HeapTuple aggTuple;
+ StorageTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
AttrNumber initvalAttNo;
@@ -2175,7 +2175,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
/* Check that aggregate owner has permission to call component fns */
{
- HeapTuple procTuple;
+ StorageTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index f3da2ddd08..31e0d7ef97 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -633,11 +633,11 @@ SPI_freeplan(SPIPlanPtr plan)
return 0;
}
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
+StorageTuple
+SPI_copytuple(StorageTuple tuple)
{
MemoryContext oldcxt;
- HeapTuple ctuple;
+ StorageTuple ctuple;
if (tuple == NULL)
{
@@ -661,7 +661,7 @@ SPI_copytuple(HeapTuple tuple)
}
HeapTupleHeader
-SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
+SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc)
{
MemoryContext oldcxt;
HeapTupleHeader dtup;
@@ -692,7 +692,7 @@ SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
return dtup;
}
-HeapTuple
+StorageTuple
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
Datum *Values, const char *Nulls)
{
@@ -860,7 +860,7 @@ char *
SPI_gettype(TupleDesc tupdesc, int fnumber)
{
Oid typoid;
- HeapTuple typeTuple;
+ StorageTuple typeTuple;
char *result;
SPI_result = 0;
@@ -968,7 +968,7 @@ SPI_datumTransfer(Datum value, bool typByVal, int typLen)
}
void
-SPI_freetuple(HeapTuple tuple)
+SPI_freetuple(StorageTuple tuple)
{
/* No longer need to worry which context tuple was in... */
heap_freetuple(tuple);
@@ -1689,7 +1689,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
/* set up initial allocations */
tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) palloc(tuptable->alloced * sizeof(StorageTuple));
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
MemoryContextSwitchTo(oldcxt);
@@ -1720,8 +1720,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/* Double the size of the pointer array */
tuptable->free = tuptable->alloced;
tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ tuptable->vals = (StorageTuple *) repalloc_huge(tuptable->vals,
+ tuptable->alloced * sizeof(StorageTuple));
}
tuptable->vals[tuptable->alloced - tuptable->free] =
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
index ef681e2ec2..41d94774cb 100644
--- a/src/backend/executor/tqueue.c
+++ b/src/backend/executor/tqueue.c
@@ -168,7 +168,7 @@ DestroyTupleQueueReader(TupleQueueReader *reader)
* accumulate bytes from a partially-read message, so it's useful to call
* this with nowait = true even if nothing is returned.
*/
-HeapTuple
+StorageTuple
TupleQueueReaderNext(TupleQueueReader *reader, bool nowait, bool *done)
{
HeapTupleData htup;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb01b..0d2e1733bf 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1883,9 +1883,9 @@ get_database_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
avw_dbase *avdb;
@@ -1912,7 +1912,7 @@ get_database_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
@@ -2043,13 +2043,13 @@ do_autovacuum(void)
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan_catalog(classRel, 0, NULL);
+ relScan = storage_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2135,7 +2135,7 @@ do_autovacuum(void)
}
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
/* second pass: check TOAST tables */
ScanKeyInit(&key,
@@ -2143,8 +2143,8 @@ do_autovacuum(void)
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan_catalog(classRel, 1, &key);
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ relScan = storage_beginscan_catalog(classRel, 1, &key);
+ while ((tuple = storage_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
PgStat_StatTabEntry *tabentry;
@@ -2190,7 +2190,7 @@ do_autovacuum(void)
table_oids = lappend_oid(table_oids, relid);
}
- heap_endscan(relScan);
+ storage_endscan(relScan);
heap_close(classRel, AccessShareLock);
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 5c256ff8ab..2b56b740a2 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -36,6 +36,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
@@ -1221,8 +1222,8 @@ pgstat_collect_oids(Oid catalogid)
rel = heap_open(catalogid, AccessShareLock);
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scan = heap_beginscan(rel, snapshot, 0, NULL);
- while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ scan = storage_beginscan(rel, snapshot, 0, NULL);
+ while ((tup = storage_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
@@ -1230,7 +1231,7 @@ pgstat_collect_oids(Oid catalogid)
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
- heap_endscan(scan);
+ storage_endscan(scan);
UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index a613ef4757..83ec2dfcbe 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -24,6 +24,7 @@
#include "access/heapam.h"
#include "access/htup.h"
#include "access/htup_details.h"
+#include "access/storageam.h"
#include "access/xact.h"
#include "catalog/pg_subscription.h"
@@ -124,9 +125,9 @@ get_subscription_list(void)
(void) GetTransactionSnapshot();
rel = heap_open(SubscriptionRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(rel, 0, NULL);
+ scan = storage_beginscan_catalog(rel, 0, NULL);
- while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ while (HeapTupleIsValid(tup = storage_getnext(scan, ForwardScanDirection)))
{
Form_pg_subscription subform = (Form_pg_subscription) GETSTRUCT(tup);
Subscription *sub;
@@ -152,7 +153,7 @@ get_subscription_list(void)
MemoryContextSwitchTo(oldcxt);
}
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(rel, AccessShareLock);
CommitTransactionCommand();
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 247809927a..c6791c758c 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/storageam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
@@ -435,13 +436,13 @@ DefineQueryRewrite(const char *rulename,
RelationGetRelationName(event_relation))));
snapshot = RegisterSnapshot(GetLatestSnapshot());
- scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
- if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
+ scanDesc = storage_beginscan(event_relation, snapshot, 0, NULL);
+ if (storage_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
- heap_endscan(scanDesc);
+ storage_endscan(scanDesc);
UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 20f1d279e9..03b7cc76d7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/session.h"
+#include "access/storageam.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1212,10 +1213,10 @@ ThereIsAtLeastOneRole(void)
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
- result = (heap_getnext(scan, ForwardScanDirection) != NULL);
+ scan = storage_beginscan_catalog(pg_authid_rel, 0, NULL);
+ result = (storage_getnext(scan, ForwardScanDirection) != NULL);
- heap_endscan(scan);
+ storage_endscan(scan);
heap_close(pg_authid_rel, AccessShareLock);
return result;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 251342b01b..be62547195 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -108,26 +108,25 @@ typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
#define HeapScanIsValid(scan) PointerIsValid(scan)
extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
- ScanKey key);
-extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync);
-extern HeapScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot,
- int nkeys, ScanKey key);
-extern HeapScanDesc heap_beginscan_sampling(Relation relation,
- Snapshot snapshot, int nkeys, ScanKey key,
- bool allow_strat, bool allow_sync, bool allow_pagemode);
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
BlockNumber endBlk);
extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
-extern void heap_rescan(HeapScanDesc scan, ScanKey key);
+extern void heap_rescan(HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode);
extern void heap_endscan(HeapScanDesc scan);
-extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
-
+extern StorageTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
+extern TupleTableSlot *heap_getnextslot(HeapScanDesc sscan, ScanDirection direction,
+ TupleTableSlot *slot);
extern Size heap_parallelscan_estimate(Snapshot snapshot);
extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
Relation relation, Snapshot snapshot);
diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h
index ff28b4dfec..7d70de4e33 100644
--- a/src/include/access/storage_common.h
+++ b/src/include/access/storage_common.h
@@ -27,6 +27,7 @@
/* A physical tuple coming from a storage AM scan */
typedef void *StorageTuple;
+typedef void *StorageScanDesc;
/*
* slot storage routine functions
diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h
index e0658a1ddd..e79a24055d 100644
--- a/src/include/access/storageam.h
+++ b/src/include/access/storageam.h
@@ -15,10 +15,59 @@
#define STORAGEAM_H
#include "access/heapam.h"
-#include "access/storageamapi.h"
+#include "access/storage_common.h"
#include "executor/tuptable.h"
#include "nodes/execnodes.h"
+typedef union tuple_data
+{
+ TransactionId xid;
+ CommandId cid;
+ ItemPointerData tid;
+} tuple_data;
+
+typedef enum tuple_data_flags
+{
+ XMIN = 0,
+ UPDATED_XID,
+ CMIN,
+ TID,
+ CTID
+} tuple_data_flags;
+
+/* Function pointer to let the index tuple insert from storage am */
+typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
+ bool *specConflict, List *arbiterIndexes);
+
+
+extern HeapScanDesc storage_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan);
+
+extern void storage_setscanlimits(HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+extern HeapScanDesc storage_beginscan(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_catalog(Relation relation, int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_strat(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync);
+extern HeapScanDesc storage_beginscan_bm(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key);
+extern HeapScanDesc storage_beginscan_sampling(Relation relation, Snapshot snapshot,
+ int nkeys, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+
+extern void storage_endscan(HeapScanDesc scan);
+extern void storage_rescan(HeapScanDesc scan, ScanKey key);
+extern void storage_rescan_set_params(HeapScanDesc scan, ScanKey key,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+extern void storage_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
+
+extern StorageTuple storage_getnext(HeapScanDesc sscan, ScanDirection direction);
+extern TupleTableSlot *storage_getnextslot(HeapScanDesc sscan, ScanDirection direction, TupleTableSlot *slot);
+
+extern void storage_get_latest_tid(Relation relation,
+ Snapshot snapshot,
+ ItemPointer tid);
+
extern bool storage_fetch(Relation relation,
ItemPointer tid,
Snapshot snapshot,
@@ -27,6 +76,13 @@ extern bool storage_fetch(Relation relation,
bool keep_buf,
Relation stats_relation);
+extern bool storage_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
+ Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+extern bool storage_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
+ bool *all_dead);
+
extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_updates,
diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h
index 22bc2493ac..bda6b46a94 100644
--- a/src/include/access/storageamapi.h
+++ b/src/include/access/storageamapi.h
@@ -12,32 +12,12 @@
#define STORAGEAMAPI_H
#include "access/heapam.h"
-#include "access/storage_common.h"
+#include "access/storageam.h"
#include "nodes/execnodes.h"
#include "nodes/nodes.h"
#include "utils/snapshot.h"
#include "fmgr.h"
-typedef union tuple_data
-{
- TransactionId xid;
- CommandId cid;
- ItemPointerData tid;
-} tuple_data;
-
-typedef enum tuple_data_flags
-{
- XMIN = 0,
- UPDATED_XID,
- CMIN,
- TID,
- CTID
-} tuple_data_flags;
-
-/* Function pointer to let the index tuple insert from storage am */
-typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr,
- bool *specConflict, List *arbiterIndexes);
-
/*
* Storage routine functions
*/
@@ -103,6 +83,39 @@ typedef void (*SpeculativeAbort_function) (Relation rel,
typedef void (*RelationSync_function) (Relation relation);
+
+typedef HeapScanDesc (*ScanBegin_function) (Relation relation,
+ Snapshot snapshot,
+ int nkeys, ScanKey key,
+ ParallelHeapScanDesc parallel_scan,
+ bool allow_strat,
+ bool allow_sync,
+ bool allow_pagemode,
+ bool is_bitmapscan,
+ bool is_samplescan,
+ bool temp_snap);
+typedef void (*ScanSetlimits_function) (HeapScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks);
+
+/* must return a TupleTableSlot? */
+typedef StorageTuple(*ScanGetnext_function) (HeapScanDesc scan,
+ ScanDirection direction);
+
+typedef TupleTableSlot *(*ScanGetnextSlot_function) (HeapScanDesc scan,
+ ScanDirection direction, TupleTableSlot *slot);
+
+typedef void (*ScanEnd_function) (HeapScanDesc scan);
+
+
+typedef void (*ScanGetpage_function) (HeapScanDesc scan, BlockNumber page);
+typedef void (*ScanRescan_function) (HeapScanDesc scan, ScanKey key, bool set_params,
+ bool allow_strat, bool allow_sync, bool allow_pagemode);
+typedef void (*ScanUpdateSnapshot_function) (HeapScanDesc scan, Snapshot snapshot);
+
+typedef bool (*HotSearchBuffer_function) (ItemPointer tid, Relation relation,
+ Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
+ bool *all_dead, bool first_call);
+
+
/*
* API struct for a storage AM. Note this must be stored in a single palloc'd
* chunk of memory.
@@ -145,6 +158,17 @@ typedef struct StorageAmRoutine
RelationSync_function relation_sync; /* heap_sync */
+ /* Operations on relation scans */
+ ScanBegin_function scan_begin;
+ ScanSetlimits_function scansetlimits;
+ ScanGetnext_function scan_getnext;
+ ScanGetnextSlot_function scan_getnextslot;
+ ScanEnd_function scan_end;
+ ScanGetpage_function scan_getpage;
+ ScanRescan_function scan_rescan;
+ ScanUpdateSnapshot_function scan_update_snapshot;
+ HotSearchBuffer_function hot_search_buffer; /* heap_hot_search_buffer */
+
} StorageAmRoutine;
extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler);
diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h
index 718d8947a3..7f9bef1374 100644
--- a/src/include/executor/functions.h
+++ b/src/include/executor/functions.h
@@ -22,7 +22,7 @@ typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
-extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
+extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(StorageTuple procedureTuple,
Node *call_expr,
Oid inputCollation);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index acade7e92e..2781975530 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -25,7 +25,7 @@ typedef struct SPITupleTable
uint64 alloced; /* # of alloced vals */
uint64 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
- HeapTuple *vals; /* tuples */
+ StorageTuple *vals; /* tuples */
slist_node next; /* link for internal bookkeeping */
SubTransactionId subid; /* subxact in which tuptable was created */
} SPITupleTable;
@@ -117,10 +117,10 @@ extern const char *SPI_result_code_string(int code);
extern List *SPI_plan_get_plan_sources(SPIPlanPtr plan);
extern CachedPlan *SPI_plan_get_cached_plan(SPIPlanPtr plan);
-extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
-extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
- int *attnum, Datum *Values, const char *Nulls);
+extern StorageTuple SPI_copytuple(StorageTuple tuple);
+extern HeapTupleHeader SPI_returntuple(StorageTuple tuple, TupleDesc tupdesc);
+extern StorageTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
+ int *attnum, Datum *Values, const char *Nulls);
extern int SPI_fnumber(TupleDesc tupdesc, const char *fname);
extern char *SPI_fname(TupleDesc tupdesc, int fnumber);
extern char *SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber);
@@ -133,7 +133,7 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
-extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuple(StorageTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
index fdc9deb2b2..9905ecd8ba 100644
--- a/src/include/executor/tqueue.h
+++ b/src/include/executor/tqueue.h
@@ -26,7 +26,7 @@ extern DestReceiver *CreateTupleQueueDestReceiver(shm_mq_handle *handle);
/* Use these to receive tuples from a shm_mq. */
extern TupleQueueReader *CreateTupleQueueReader(shm_mq_handle *handle);
extern void DestroyTupleQueueReader(TupleQueueReader *reader);
-extern HeapTuple TupleQueueReaderNext(TupleQueueReader *reader,
- bool nowait, bool *done);
+extern StorageTuple TupleQueueReaderNext(TupleQueueReader *reader,
+ bool nowait, bool *done);
#endif /* TQUEUE_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 223eef28d1..54b37f7db5 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -237,7 +237,7 @@ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
/* from execTuples.c */
extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
-extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+extern StorageTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
--
2.15.0.windows.1
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2017-12-27 12:33 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-12-27 12:33 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hi!
On Wed, Dec 27, 2017 at 6:54 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> On Tue, Dec 12, 2017 at 3:06 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
>>
>> I restructured that patch files to avoid showing unnecessary
>> modifications,
>> and also it will be easy for adding of new API's based on the all the
>> functions
>> that are exposed by heapam module easily compared earlier.
>>
>> Attached are the latest set of patches. I will work on the remaining
>> pending
>> items.
>>
>
> Apart from rebase to the latest master code, following are the additional
> changes,
>
> 1. Added API for bulk insert and rewrite functionality(Logical rewrite is
> not touched yet)
> 2. Tuple lock API interface redesign to remove the traversal logic from
> executor module.
>
Great, thank you!
> The tuple lock API interface changes are from "Alexander Korotkov" from
> "PostgresPro".
> Thanks Alexander. Currently we both are doing joint development for faster
> closure of
> open items that are pending to bring the "pluggable storage API" into a
> good shape.
>
Thank you for announcing this. Yes, pluggable storage API requires a lot
of work to get into committable shape. This is why I've decided to join
the development.
Let me explain the idea behind new tuple lock API and further patches I
plan to send. As I noted upthread, I consider possibility of alternative
MVCC implementations as vital property of pluggable storage API. These
include undo log option when tuple is updated in-place while old version of
tuple is displaced to some special area. In this case, new version of
tuple would reside on same TID as old version of tuple. This is an
important point, because TID is not really tuple identifier anymore.
Naturally, TID becomes a row identifier while tuple may be identified by
pair (tid, snapshot). For current heap, snapshot is redundant and can be
used just for assert checking (tuple on given tid is really visible using
given snapshot). For heap with undo log, appropriate tuple could be found
by snapshot in the undo chain associated with given tid.
One of consequences of above is that we cannot use fact that tid isn't
changed after update as sign that tuple was deleted. This is why I've
introduced HTSU_Result HeapTupleDeleted. Another consequence is that our
tid traverse logic in the executor layer is not valid anymore. For
instance, this traversal from older tuple to latter tuple doesn't make any
sense for heap with undo log where latter tuple is more easily accessible
than older tuple. This is why I decided to hide this logic in storage
layer and provide TUPLE_LOCK_FLAG_FIND_LAST_VERSION flag which indicates
that lock_tuple() have to find latest updated version and lock it. I've
also changed follow_updates bool to more explicit
TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS flag in order to not mess it with
previous flag which also kind of follow updates. Third consequence is that
we have to pass snapshot to tuple_update() and tuple_delete() methods to
let them check if row was concurrently updated while residing on the same
TID. I'm going to provide this change as separate patch.
Also, I appreciate that now tuple_insert() and tuple_update() methods are
responsible for inserting index tuples. This unleash pluggable storages to
implement another way of interaction with indexes. However, I didn't get
the point of passing InsertIndexTuples IndexFunc to them. Now, we're
always passing ExecInsertIndexTuples() to this argument. As I understood
storage is free to either call ExecInsertIndexTuples() or implement its own
logic of interaction with indexes. But, I don't understand why do we need
a callback when tuple_insert() and tuple_update() can call
ExecInsertIndexTuples() directly if needed. Another thing is that
tuple_delete() could also interact with indexes (especially when we will
enhance index access method API), and we need to pass meta-information
about indexes to tuple_delete() too.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-01-03 23:00 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 05:03 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2018-01-03 23:00 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Jan 3, 2018 at 10:08 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> On Wed, Dec 27, 2017 at 11:33 PM, Alexander Korotkov <
> a.korotkov@postgrespro.ru> wrote:
>
>>
>> Also, I appreciate that now tuple_insert() and tuple_update() methods are
>> responsible for inserting index tuples. This unleash pluggable storages to
>> implement another way of interaction with indexes. However, I didn't get
>> the point of passing InsertIndexTuples IndexFunc to them. Now, we're
>> always passing ExecInsertIndexTuples() to this argument. As I
>> understood storage is free to either call ExecInsertIndexTuples() or
>> implement its own logic of interaction with indexes. But, I don't
>> understand why do we need a callback when tuple_insert() and tuple_update()
>> can call ExecInsertIndexTuples() directly if needed. Another thing is that
>> tuple_delete() could also interact with indexes (especially when we will
>> enhance index access method API), and we need to pass meta-information
>> about indexes to tuple_delete() too.
>>
>
> The main reason for which I added the callback function to not to
> introduce the
> dependency of storage on executor functions. This way storage can call the
> function that is passed to it without any knowledge. I added the function
> pointer
> for tuple_delete also in the new patches, currently it is passed as NULL
> for heap.
> These API's can be enhanced later.
>
Understood, but in order to implement alternative behavior with indexes
(for example,
insert index tuples to only some of indexes), storage am will still have to
call executor
functions. So, yes this needs to be enhanced. Probably, we just need to
implement
nicer executor API for storage am.
> Apart from rebase, Added storage shared memory API, currently this API is
> used
> only by the syncscan. And also all the exposed functions of syncscan usage
> is
> removed outside the heap.
>
This makes me uneasy. You introduce two new hooks for size estimation and
initialization
of shared memory needed by storage am's. But if storage am is implemented
in shared library,
then this shared library can use our generic method for allocation of
shared memory
(including memory needed by storage am). If storage am is builtin, then
hooks are also not
needed, because we know all our builtin storage am's in advance. For me,
it would be
nice to encapsulate heap am requirements in shared memory into functions
like
HeapAmShmemSize() and HeapAmShmemInit(), and don't explicitly show outside
that
this memory is needed for synchronized scan. But separate hooks don't look
justified for me.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-03 23:00 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2018-01-04 05:03 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-04 22:55 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2018-01-04 05:03 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jan 4, 2018 at 10:00 AM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:
> On Wed, Jan 3, 2018 at 10:08 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
>>
>> On Wed, Dec 27, 2017 at 11:33 PM, Alexander Korotkov <
>> a.korotkov@postgrespro.ru> wrote:
>>
>>>
>>> Also, I appreciate that now tuple_insert() and tuple_update() methods
>>> are responsible for inserting index tuples. This unleash pluggable
>>> storages to implement another way of interaction with indexes. However, I
>>> didn't get the point of passing InsertIndexTuples IndexFunc to them. Now,
>>> we're always passing ExecInsertIndexTuples() to this argument. As I
>>> understood storage is free to either call ExecInsertIndexTuples() or
>>> implement its own logic of interaction with indexes. But, I don't
>>> understand why do we need a callback when tuple_insert() and tuple_update()
>>> can call ExecInsertIndexTuples() directly if needed. Another thing is that
>>> tuple_delete() could also interact with indexes (especially when we will
>>> enhance index access method API), and we need to pass meta-information
>>> about indexes to tuple_delete() too.
>>>
>>
>> The main reason for which I added the callback function to not to
>> introduce the
>> dependency of storage on executor functions. This way storage can call the
>> function that is passed to it without any knowledge. I added the function
>> pointer
>> for tuple_delete also in the new patches, currently it is passed as NULL
>> for heap.
>> These API's can be enhanced later.
>>
>
> Understood, but in order to implement alternative behavior with indexes
> (for example,
> insert index tuples to only some of indexes), storage am will still have
> to call executor
> functions. So, yes this needs to be enhanced. Probably, we just need to
> implement
> nicer executor API for storage am.
>
OK.
> Apart from rebase, Added storage shared memory API, currently this API is
>> used
>> only by the syncscan. And also all the exposed functions of syncscan
>> usage is
>> removed outside the heap.
>>
>
> This makes me uneasy. You introduce two new hooks for size estimation and
> initialization
> of shared memory needed by storage am's. But if storage am is implemented
> in shared library,
> then this shared library can use our generic method for allocation of
> shared memory
> (including memory needed by storage am). If storage am is builtin, then
> hooks are also not
> needed, because we know all our builtin storage am's in advance. For me,
> it would be
> nice to encapsulate heap am requirements in shared memory into functions
> like
> HeapAmShmemSize() and HeapAmShmemInit(), and don't explicitly show outside
> that
> this memory is needed for synchronized scan. But separate hooks don't
> look justified for me.
>
Yes, I agree that for the builtin storage's there is no need of hooks. But
in future,
if we want to support multiple storage's in an instance, we may need hooks
for shared memory
registration. I am fine to change it.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-03 23:00 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 05:03 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-01-04 22:55 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 23:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2018-01-04 22:55 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jan 4, 2018 at 8:03 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
> On Thu, Jan 4, 2018 at 10:00 AM, Alexander Korotkov <
> a.korotkov@postgrespro.ru> wrote:
>
>> On Wed, Jan 3, 2018 at 10:08 AM, Haribabu Kommi <kommi.haribabu@gmail.com
>> > wrote:
>>
>>> Apart from rebase, Added storage shared memory API, currently this API
>>> is used
>>>
>> only by the syncscan. And also all the exposed functions of syncscan
>>> usage is
>>> removed outside the heap.
>>>
>>
>> This makes me uneasy. You introduce two new hooks for size estimation
>> and initialization
>> of shared memory needed by storage am's. But if storage am is
>> implemented in shared library,
>> then this shared library can use our generic method for allocation of
>> shared memory
>> (including memory needed by storage am). If storage am is builtin, then
>> hooks are also not
>> needed, because we know all our builtin storage am's in advance. For me,
>> it would be
>> nice to encapsulate heap am requirements in shared memory into functions
>> like
>> HeapAmShmemSize() and HeapAmShmemInit(), and don't explicitly show
>> outside that
>> this memory is needed for synchronized scan. But separate hooks don't
>> look justified for me.
>>
>
> Yes, I agree that for the builtin storage's there is no need of hooks. But
> in future,
> if we want to support multiple storage's in an instance, we may need hooks
> for shared memory
> registration. I am fine to change it.
>
Yes, but we already have hooks for shared memory registration in shared
modules. I don't see the point for another hooks for the same purpose.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-03 23:00 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 05:03 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-04 22:55 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2018-01-04 23:20 ` Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 0 replies; 138+ messages in thread
From: Haribabu Kommi @ 2018-01-04 23:20 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Jan 5, 2018 at 9:55 AM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:
> On Thu, Jan 4, 2018 at 8:03 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
>> On Thu, Jan 4, 2018 at 10:00 AM, Alexander Korotkov <
>> a.korotkov@postgrespro.ru> wrote:
>>
>>> On Wed, Jan 3, 2018 at 10:08 AM, Haribabu Kommi <
>>> kommi.haribabu@gmail.com> wrote:
>>>
>>>> Apart from rebase, Added storage shared memory API, currently this API
>>>> is used
>>>>
>>> only by the syncscan. And also all the exposed functions of syncscan
>>>> usage is
>>>> removed outside the heap.
>>>>
>>>
>>> This makes me uneasy. You introduce two new hooks for size estimation
>>> and initialization
>>> of shared memory needed by storage am's. But if storage am is
>>> implemented in shared library,
>>> then this shared library can use our generic method for allocation of
>>> shared memory
>>> (including memory needed by storage am). If storage am is builtin, then
>>> hooks are also not
>>> needed, because we know all our builtin storage am's in advance. For
>>> me, it would be
>>> nice to encapsulate heap am requirements in shared memory into functions
>>> like
>>> HeapAmShmemSize() and HeapAmShmemInit(), and don't explicitly show
>>> outside that
>>> this memory is needed for synchronized scan. But separate hooks don't
>>> look justified for me.
>>>
>>
>> Yes, I agree that for the builtin storage's there is no need of hooks.
>> But in future,
>> if we want to support multiple storage's in an instance, we may need
>> hooks for shared memory
>> registration. I am fine to change it.
>>
>
> Yes, but we already have hooks for shared memory registration in shared
> modules. I don't see the point for another hooks for the same purpose.
>
Oh, yes, I missed it. I will update the patch and share it later.
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-01-05 16:20 ` Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Robert Haas @ 2018-01-05 16:20 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
I do not like the way that this patch set uses the word "storage". In
current usage, storage is a thing that certain kinds of relations
have. Plain relations (a.k.a. heap tables) have storage, indexes have
storage, materialized views have storage, TOAST tables have storage,
and sequences have storage. This patch set wants to use "storage AM"
to mean a replacement for a plain heap table, but I think that's going
to create a lot of confusion, because it overlaps heavily with the
existing meaning yet is different. My suggestion is to call these
"table access methods" rather than "storage access methods". Then,
the default table AM can be heap. This has the nice property that you
create an index using CREATE INDEX and the support functions arrive
via an IndexAmRoutine, so correspondingly you would create a table
using CREATE TABLE and the support functions would arrive via a
TableAmRoutine -- so all the names match.
An alternative would be to call the new thing a "heap AM" with
HeapAmRoutine as the support function structure. That's also not
unreasonable. In that case, we're deciding that "heap" is not just
the name of the current implementation, but the name of the kind of
thing that backs a table at the storage level. I don't like that
quite as well because then we've got a class of things called a heap
of which the current and only implementation is called heap, which is
a bit confusing in my view. But we could probably make it work.
If we adopt the first proposal, it leads to another nice parallel: we
can have src/backend/access/table for those things which are generic
to table AMs, just as we have src/backend/access/index for things
which are generic to index AMs, and then src/backend/access/<am-name>
for things which are specific to a particular AM.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2018-01-05 19:31 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2018-01-05 19:31 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Jan 5, 2018 at 7:20 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> I do not like the way that this patch set uses the word "storage". In
> current usage, storage is a thing that certain kinds of relations
> have. Plain relations (a.k.a. heap tables) have storage, indexes have
> storage, materialized views have storage, TOAST tables have storage,
> and sequences have storage. This patch set wants to use "storage AM"
> to mean a replacement for a plain heap table, but I think that's going
> to create a lot of confusion, because it overlaps heavily with the
> existing meaning yet is different.
Good point, thank you for noticing that. Name "storage" is really confusing
for this purpose.
> My suggestion is to call these
> "table access methods" rather than "storage access methods". Then,
> the default table AM can be heap. This has the nice property that you
> create an index using CREATE INDEX and the support functions arrive
> via an IndexAmRoutine, so correspondingly you would create a table
> using CREATE TABLE and the support functions would arrive via a
> TableAmRoutine -- so all the names match.
>
> An alternative would be to call the new thing a "heap AM" with
> HeapAmRoutine as the support function structure. That's also not
> unreasonable. In that case, we're deciding that "heap" is not just
> the name of the current implementation, but the name of the kind of
> thing that backs a table at the storage level. I don't like that
> quite as well because then we've got a class of things called a heap
> of which the current and only implementation is called heap, which is
> a bit confusing in my view. But we could probably make it work.
>
> If we adopt the first proposal, it leads to another nice parallel: we
> can have src/backend/access/table for those things which are generic
> to table AMs, just as we have src/backend/access/index for things
> which are generic to index AMs, and then src/backend/access/<am-name>
> for things which are specific to a particular AM.
I would vote for the first proposal: table AM. Because we eventually
might get index-organized tables whose don't have something like heap.
So, it would be nice to avoid hardcoding "heap" name.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-02-05 11:22 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Haribabu Kommi @ 2018-02-05 11:22 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Jan 9, 2018 at 11:42 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> Updated patches are attached.
>
To integrate the columnar store with the pluggable storage API, I found that
there are couple of other things also that needs to be supported.
1. Choosing the right table access method for a particular table?
I am thinking of adding a new table option to let the user select the
correct table
access method that the user wants for the table. HEAP is the default access
method. This approach may be simple and doesn't need any syntax changes.
Or Is it fine to add syntax "USING method" to CREATE TABLE similar like
CREATE INDEX?
comments?
2. As the columnar storage needs many other relations that are needs to be
created along with main relation.
As these extra relations are internal to the storage and shouldn't be
visible
directly from pg_class and these will be stored in the storage specific
catalog tables. A dependency is created for the original table as these
storage
specific tables must be created/dropped/altered whenever there is a change
with the original table.
Is it fine to add new API while creating/altering/drop the table to get the
control?
or to use only exiting processutility hook?
Regards,
Hari Babu
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-02-16 10:56 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-21 00:29 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Alexander Korotkov @ 2018-02-16 10:56 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Hi, Haribabu!
On Mon, Feb 5, 2018 at 2:22 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
>
> On Tue, Jan 9, 2018 at 11:42 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
>>
>> Updated patches are attached.
>>
>
> To integrate the columnar store with the pluggable storage API, I found
> that
> there are couple of other things also that needs to be supported.
>
> 1. Choosing the right table access method for a particular table?
>
> I am thinking of adding a new table option to let the user select the
> correct table
> access method that the user wants for the table. HEAP is the default access
> method. This approach may be simple and doesn't need any syntax changes.
>
> Or Is it fine to add syntax "USING method" to CREATE TABLE similar like
> CREATE INDEX?
>
> comments?
>
Sure, user needs to specify particular table access method when creating a
table. "USING method" is good for me. I think it's better to reuse
already existing syntax rather than reinventing something new.
> 2. As the columnar storage needs many other relations that are needs to be
> created along with main relation.
>
That's an interesting point. During experimenting on some new index access
methods I also have to define some kind of "internal" relations invisible
for user, but essential for main relation functionality. I've made them in
hackery manner, and I think legal mechanism for them would be very good.
> As these extra relations are internal to the storage and shouldn't be
> visible
> directly from pg_class and these will be stored in the storage specific
> catalog tables. A dependency is created for the original table as these
> storage
> specific tables must be created/dropped/altered whenever there is a change
> with the original table.
>
I think that internal relations should be visible from pg_class, otherwise
it wouldn't be possible to define dependencies over them. But they should
have different relkind, so they wouldn't be messed up with regular
relations.
Is it fine to add new API while creating/altering/drop the table to get the
> control?
> or to use only exiting processutility hook?
>
I believe that we need some generic way for defining internal relations,
not hooks. For me it seems like useful feature for further development of
both index access methods and table access methods.
During developer meeting [1], I've proposed to reorder patches so that
refactoring patches go first and API introduction patches go afterwards.
That would make possible to commit some of refactoring patches earlier
without necessary committing API in the same release cycle. If no
objection, I would do that reordering.
BTW, EnterpriseDB announces zheap table access method (heap with undo log)
[2]. I think this is great, and I'm looking forward for publishing zheap
in mailing lists. But I'm concerning about its compatibility with
pluggable table access methods API. Does zheap use table AM API from this
thread? Or does it just override current heap and needs to be adopted to
use table AM API? Or does it implements own API?
*Links*
1. https://wiki.postgresql.org/wiki/FOSDEM/PGDay_2018_
Developer_Meeting#Minutes
2. http://rhaas.blogspot.com.by/2018/01/do-or-undo-there-is-no-vacuum.html
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2018-02-22 23:20 ` Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Robert Haas @ 2018-02-22 23:20 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Feb 16, 2018 at 5:56 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> BTW, EnterpriseDB announces zheap table access method (heap with undo log)
> [2]. I think this is great, and I'm looking forward for publishing zheap in
> mailing lists. But I'm concerning about its compatibility with pluggable
> table access methods API. Does zheap use table AM API from this thread? Or
> does it just override current heap and needs to be adopted to use table AM
> API? Or does it implements own API?
Right now it just hacks the code. The plan is to adapt it to whatever
API we settle on in this thread.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2018-02-26 08:19 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2018-02-26 08:19 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Feb 23, 2018 at 2:20 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Feb 16, 2018 at 5:56 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > BTW, EnterpriseDB announces zheap table access method (heap with undo
> log)
> > [2]. I think this is great, and I'm looking forward for publishing
> zheap in
> > mailing lists. But I'm concerning about its compatibility with pluggable
> > table access methods API. Does zheap use table AM API from this
> thread? Or
> > does it just override current heap and needs to be adopted to use table
> AM
> > API? Or does it implements own API?
>
> Right now it just hacks the code. The plan is to adapt it to whatever
> API we settle on in this thread.
Great, thank you for clarification. I'm looking forward reviewing zheap :)
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2018-03-28 16:23 ` David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
0 siblings, 1 reply; 138+ messages in thread
From: David Steele @ 2018-03-28 16:23 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 2/26/18 3:19 AM, Alexander Korotkov wrote:
> On Fri, Feb 23, 2018 at 2:20 AM, Robert Haas <robertmhaas@gmail.com
> <mailto:robertmhaas@gmail.com>> wrote:
>
> On Fri, Feb 16, 2018 at 5:56 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru <mailto:a.korotkov@postgrespro.ru>> wrote:
> > BTW, EnterpriseDB announces zheap table access method (heap with undo log)
> > [2]. I think this is great, and I'm looking forward for publishing zheap in
> > mailing lists. But I'm concerning about its compatibility with pluggable
> > table access methods API. Does zheap use table AM API from this thread? Or
> > does it just override current heap and needs to be adopted to use table AM
> > API? Or does it implements own API?
>
> Right now it just hacks the code. The plan is to adapt it to whatever
> API we settle on in this thread.
>
>
> Great, thank you for clarification. I'm looking forward reviewing zheap :)
I think this entry should be moved the the next CF. I'll do that
tomorrow unless there are objections.
Regards,
--
-David
david@pgmasters.net
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
@ 2018-03-29 00:39 ` Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-03-29 13:07 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
0 siblings, 2 replies; 138+ messages in thread
From: Michael Paquier @ 2018-03-29 00:39 UTC (permalink / raw)
To: David Steele <david@pgmasters.net>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, Mar 28, 2018 at 12:23:56PM -0400, David Steele wrote:
> I think this entry should be moved the the next CF. I'll do that
> tomorrow unless there are objections.
Instead of moving things to the next CF by default, perhaps it would
make more sense to mark things as reviewed with feedback as this is the
last CF? There is a 5-month gap between this commit fest and the next
one, I am getting afraid of flooding the beginning of v12 development
cycle with entries which keep rotting over time. If the author(s) claim
that they will be able to work on it, then that's of course fine.
Sorry for the digression, patches ignored across CFs contribute to the
bloat we see, and those eat the time of the CFM.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (832B, ../../20180329003944.GG2102@paquier.xyz/2-signature.asc)
download
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
@ 2018-06-14 02:25 ` Amit Kapila <amit.kapila16@gmail.com>
2018-06-18 19:43 ` Re: Pluggable storage AJG <ayden@gera.co.nz>
2018-06-22 04:24 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Amit Kapila @ 2018-06-14 02:25 UTC (permalink / raw)
To: Haribabu Kommi <kommi.haribabu@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Robert Haas <robertmhaas@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 14, 2018 at 1:50 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:
>
> On Fri, Apr 20, 2018 at 4:44 PM Haribabu Kommi <kommi.haribabu@gmail.com>
> wrote:
>
> VACUUM:
> Not much changes are done in this apart moving the Vacuum visibility
> functions as part of the
> storage. But idea for the Vacuum was with each access method can define how
> it should perform.
>
We are planning to have a somewhat different mechanism for vacuum (for
non-delete marked indexes), so if you can provide some details or
discuss what you have in mind before implementation of same, that
would be great.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-14 02:25 ` Re: [HACKERS] Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2018-06-18 19:43 ` AJG <ayden@gera.co.nz>
2018-06-18 20:14 ` Re: Pluggable storage Andres Freund <andres@anarazel.de>
2018-06-19 03:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
1 sibling, 2 replies; 138+ messages in thread
From: AJG @ 2018-06-18 19:43 UTC (permalink / raw)
To: pgsql-hackers
@Amit
Re: Vacuum etc.
Chrome V8 just released this blog post around concurrent marking, which may
be of interest considering how cpu limited the browser is. Contains
benchmark numbers etc in post as well.
https://v8project.blogspot.com/2018/06/concurrent-marking.html
"This post describes the garbage collection technique called concurrent
marking. The optimization allows a JavaScript application to continue
execution while the garbage collector scans the heap to find and mark live
objects. Our benchmarks show that concurrent marking reduces the time spent
marking on the main thread by 60%–70%"
--
Sent from: http://www.postgresql-archive.org/PostgreSQL-hackers-f1928748.html
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-14 02:25 ` Re: [HACKERS] Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2018-06-18 19:43 ` Re: Pluggable storage AJG <ayden@gera.co.nz>
@ 2018-06-18 20:14 ` Andres Freund <andres@anarazel.de>
1 sibling, 0 replies; 138+ messages in thread
From: Andres Freund @ 2018-06-18 20:14 UTC (permalink / raw)
To: AJG <ayden@gera.co.nz>; +Cc: pgsql-hackers
On 2018-06-18 12:43:57 -0700, AJG wrote:
> @Amit
>
> Re: Vacuum etc.
>
> Chrome V8 just released this blog post around concurrent marking, which may
> be of interest considering how cpu limited the browser is. Contains
> benchmark numbers etc in post as well.
>
> https://v8project.blogspot.com/2018/06/concurrent-marking.html
>
> "This post describes the garbage collection technique called concurrent
> marking. The optimization allows a JavaScript application to continue
> execution while the garbage collector scans the heap to find and mark live
> objects. Our benchmarks show that concurrent marking reduces the time spent
> marking on the main thread by 60%–70%"
I don't see how in-memory GC techniques have much bearing on the
discussion here?
Greetings,
Andres Freund
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-14 02:25 ` Re: [HACKERS] Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2018-06-18 19:43 ` Re: Pluggable storage AJG <ayden@gera.co.nz>
@ 2018-06-19 03:59 ` Amit Kapila <amit.kapila16@gmail.com>
1 sibling, 0 replies; 138+ messages in thread
From: Amit Kapila @ 2018-06-19 03:59 UTC (permalink / raw)
To: AJG <ayden@gera.co.nz>; +Cc: pgsql-hackers
On Tue, Jun 19, 2018 at 1:13 AM, AJG <ayden@gera.co.nz> wrote:
> @Amit
>
> Re: Vacuum etc.
>
> Chrome V8 just released this blog post around concurrent marking, which may
> be of interest considering how cpu limited the browser is. Contains
> benchmark numbers etc in post as well.
>
> https://v8project.blogspot.com/2018/06/concurrent-marking.html
>
> "This post describes the garbage collection technique called concurrent
> marking. The optimization allows a JavaScript application to continue
> execution while the garbage collector scans the heap to find and mark live
> objects. Our benchmarks show that concurrent marking reduces the time spent
> marking on the main thread by 60%–70%"
>
Thanks for sharing the link, but I don't think it is not directly
related to the work we are doing, but feel free to discuss it on zheap
thread [1] or even you can start a new thread, because it appears more
like some general technique to improve GC (garbage collection) rather
than something directly related zheap (or undo) technology.
[1] - https://www.postgresql.org/message-id/CAA4eK1%2BYtM5vxzSM2NZm%2BpC37MCwyvtkmJrO_yRBQeZDp9Wa2w%40mail...
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-14 02:25 ` Re: [HACKERS] Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
@ 2018-06-22 04:24 ` Haribabu Kommi <kommi.haribabu@gmail.com>
1 sibling, 0 replies; 138+ messages in thread
From: Haribabu Kommi @ 2018-06-22 04:24 UTC (permalink / raw)
To: Amit Kapila <amit.kapila16@gmail.com>; +Cc: Michael Paquier <michael@paquier.xyz>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Robert Haas <robertmhaas@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; Andres Freund <andres@anarazel.de>
On Thu, Jun 14, 2018 at 12:25 PM Amit Kapila <amit.kapila16@gmail.com>
wrote:
> On Thu, Jun 14, 2018 at 1:50 AM, Haribabu Kommi
> <kommi.haribabu@gmail.com> wrote:
> >
> > On Fri, Apr 20, 2018 at 4:44 PM Haribabu Kommi <kommi.haribabu@gmail.com
> >
> > wrote:
> >
> > VACUUM:
> > Not much changes are done in this apart moving the Vacuum visibility
> > functions as part of the
> > storage. But idea for the Vacuum was with each access method can define
> how
> > it should perform.
> >
>
> We are planning to have a somewhat different mechanism for vacuum (for
> non-delete marked indexes), so if you can provide some details or
> discuss what you have in mind before implementation of same, that
> would be great.
>
OK. Thanks for your input. We will discuss the changes before proceed to
code.
Apart from the this, the pluggable storage API contains some re-factored
changes
along with API, some of the re-factored changes are
1. Change the snapshot satisfies type from function to an enum
2. Try to return always the palloced tuple instead of a pointer to buffer
(This change may have performance impact,so can be done later).
3. Perform a tuple visibility check at heap itself for the page mode
scenario also
4. New function ExecSlotCompare to compare two slots or tuple by storing it
a temp slot.
5. heap_fetch and heap_lock_tuple returns the palloced tuple, not the
pointer to the buffer
6. The index insertion logic decision is moved into heap itself(insert,
update), not in executor.
7. Split HeapscanDesc into two and remove it's usage outside heap of the
second split
8. Move the tuple traversing and providing the updated record to heap.
Is it fine to create these changes as separate patches and can go if the
changes are fine
and doesn't have any impact?
Any comments or additions or deletions to the above list?
Regards,
Haribabu Kommi
Fujitsu Australia
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Re: [HACKERS] Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-22 23:20 ` Re: [HACKERS] Pluggable storage Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Re: [HACKERS] Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` Re: [HACKERS] Pluggable storage David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Re: [HACKERS] Pluggable storage Michael Paquier <michael@paquier.xyz>
@ 2018-03-29 13:07 ` David Steele <david@pgmasters.net>
1 sibling, 0 replies; 138+ messages in thread
From: David Steele @ 2018-03-29 13:07 UTC (permalink / raw)
To: Michael Paquier <michael@paquier.xyz>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Michael Paquier <michael.paquier@gmail.com>; Amit Kapila <amit.kapila16@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On 3/28/18 8:39 PM, Michael Paquier wrote:
> On Wed, Mar 28, 2018 at 12:23:56PM -0400, David Steele wrote:
>> I think this entry should be moved the the next CF. I'll do that
>> tomorrow unless there are objections.
>
> Instead of moving things to the next CF by default, perhaps it would
> make more sense to mark things as reviewed with feedback as this is the
> last CF? There is a 5-month gap between this commit fest and the next
> one, I am getting afraid of flooding the beginning of v12 development
> cycle with entries which keep rotting over time. If the author(s) claim
> that they will be able to work on it, then that's of course fine.
I agree and I do my best to return patches that have stalled, but I
don't think this patch is in that category. It has gotten review and
has been kept up to date. I don't think it's a good fit for v11 but I'd
like to see it in the first CF for v12.
> Sorry for the digression, patches ignored across CFs contribute to the
> bloat we see, and those eat the time of the CFM.
There's no question that bloat has become a problem. I don't have all
the answers, but vigilance by the CFMs is certainly a good start.
Regards,
--
-David
david@pgmasters.net
Attachments:
[application/pgp-signature] signature.asc (873B, ../../a1c4e8b0-c98a-8351-38e7-133bd2c438d2@pgmasters.net/2-signature.asc)
download
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-11-14 10:42 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2017-11-14 13:17 ` Re: [HACKERS] Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Alvaro Herrera @ 2017-11-14 10:42 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Amit Kapila <amit.kapila16@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; pgsql-hackers
Hmm. Am I reading it right that this discussion led to moving
essentially all code from tqual.c to heapam? Given the hard time we've
had to get tqual.c right, it seems fundamentally misguided to me to
require that every single storage AM reimplements all the visibility
routines.
I think that changing tqual's API (such as not passing HeapTuples
anymore but some other more general representation) would be okay and
should be sufficient, but this wholesale movement of code seems
dangerous and wasteful in terms of future reimplementations that will be
necessary.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: [HACKERS] Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-20 03:47 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-11-14 10:42 ` Re: [HACKERS] Pluggable storage Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2017-11-14 13:17 ` Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 0 replies; 138+ messages in thread
From: Amit Kapila @ 2017-11-14 13:17 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alexander Korotkov <a.korotkov@postgrespro.ru>; Thomas Munro <thomas.munro@enterprisedb.com>; pgsql-hackers
On Tue, Nov 14, 2017 at 4:12 PM, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> Hmm. Am I reading it right that this discussion led to moving
> essentially all code from tqual.c to heapam? Given the hard time we've
> had to get tqual.c right, it seems fundamentally misguided to me to
> require that every single storage AM reimplements all the visibility
> routines.
>
> I think that changing tqual's API (such as not passing HeapTuples
> anymore but some other more general representation) would be okay and
> should be sufficient, but this wholesale movement of code seems
> dangerous and wasteful in terms of future reimplementations that will be
> necessary.
>
I don't think the idea is to touch existing tqual.c in any significant
way. However, some other storage engine might need a different way to
check the visibility of tuples so we need provision for that. I think
for storage engine where tuple headers no longer contain transaction
information and or the old versions of tuples are chained in separate
storage (say undo storage), current visibility routines can be used.
I think the current tqual.c is quite tightly coupled with the
HeapTuple representation, so changing that or adding more code to it
for another storage engine with different tuple format won't be of
much use.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-13 17:59 ` Peter Geoghegan <pg@bowt.ie>
2017-10-13 18:41 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 138+ messages in thread
From: Peter Geoghegan @ 2017-10-13 17:59 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Oct 12, 2017 at 2:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Oct 12, 2017 at 4:38 PM, Alexander Korotkov
>> However I imply that alternative storage would share our "MVCC model". So,
>> it
>> should share our transactional model including transactions,
>> subtransactions, snapshots etc.
>> Therefore, if alternative storage is transactional, then in particular it
>> should be able to fetch tuple with
>> given TID according to given snapshot. However, how it's implemented
>> internally is
>> a black box for us. Thus, we don't insist that tuple should have different
>> TID after update;
>> we don't insist there is any analogue of HOT; we don't insist alternative
>> storage needs vacuum
>> (or if even it needs vacuum, it might be performed in completely different
>> way) and so on.
>
> Fully agreed.
If we implement that interface, where does that leave EvalPlanQual()?
Do those semantics have to be preserved?
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 17:59 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
@ 2017-10-13 18:41 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 19:39 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-10-13 18:41 UTC (permalink / raw)
To: Peter Geoghegan <pg@bowt.ie>; +Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 1:59 PM, Peter Geoghegan <pg@bowt.ie> wrote:
>> Fully agreed.
>
> If we implement that interface, where does that leave EvalPlanQual()?
> Do those semantics have to be preserved?
For a general-purpose heap storage format, I would say yes.
I mean, we don't really have control over how people use the API. If
somebody decides to implement a storage API that breaks EvalPlanQual
semantics horribly, I can't stop them, and I don't want to stop them.
Open source FTW.
But I don't really want that code in our tree, either. I think a
storage engine is and should be about the format in which data gets
stored on disk, and that it should only affect the performance of
queries not the answers that they give. I am sure there will be cases
where, for reasons of implementation complexity, that turns out not to
be true, but I think in general we should try to avoid it as much as
we can.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 17:59 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-10-13 18:41 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-10-13 19:39 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-20 03:56 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-10-13 19:39 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Peter Geoghegan <pg@bowt.ie>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Fri, Oct 13, 2017 at 9:41 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Oct 13, 2017 at 1:59 PM, Peter Geoghegan <pg@bowt.ie> wrote:
> >> Fully agreed.
> >
> > If we implement that interface, where does that leave EvalPlanQual()?
>
From the first glance, it seems that pluggable storage should
override EvalPlanQualFetch(), rest of EvalPlanQual() looks quite generic.
> > Do those semantics have to be preserved?
>
> For a general-purpose heap storage format, I would say yes.
+1
I mean, we don't really have control over how people use the API. If
> somebody decides to implement a storage API that breaks EvalPlanQual
> semantics horribly, I can't stop them, and I don't want to stop them.
> Open source FTW.
>
Yeah. We don't have any kind of "safe extensions". Any extension can
break things really horribly.
For me that means user should absolutely trust extension developer.
But I don't really want that code in our tree, either.
We keep things in our tree as correct as we can. And for sure, we should
follow this politics for pluggable storages too.
> I think a
> storage engine is and should be about the format in which data gets
> stored on disk, and that it should only affect the performance of
> queries not the answers that they give.
Pretty same idea as index access methods. They also affects the
performance, but not query answers. When it's not true, this situation
is considered as bug, and it needs to be fixed.
> I am sure there will be cases
> where, for reasons of implementation complexity, that turns out not to
> be true, but I think in general we should try to avoid it as much as
> we can.
I think in some cases we can tolerate missing features (and document it),
but don't tolerate wrong features. For instance, we may have some pluggable
storage which doesn't support transactions at all (and that should be
documented for sure), but we shouldn't have pluggable storage which
transaction support is wrong.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-12 20:38 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 17:59 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-10-13 18:41 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-10-13 19:39 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-10-20 03:56 ` Amit Kapila <amit.kapila16@gmail.com>
0 siblings, 0 replies; 138+ messages in thread
From: Amit Kapila @ 2017-10-20 03:56 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Peter Geoghegan <pg@bowt.ie>; Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Oct 14, 2017 at 1:09 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> On Fri, Oct 13, 2017 at 9:41 PM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>> On Fri, Oct 13, 2017 at 1:59 PM, Peter Geoghegan <pg@bowt.ie> wrote:
>> >> Fully agreed.
>> >
>> > If we implement that interface, where does that leave EvalPlanQual()?
>
>
> From the first glance, it seems that pluggable storage should override
> EvalPlanQualFetch(), rest of EvalPlanQual() looks quite generic.
>
I think there is more to it. Currently, EState->es_epqTuple is a
HeapTuple which is filled as part of EvalPlanQual mechanism and then
later used during the scan. We need to make it pluggable in some way
so that other heaps can work. We also need some work for
EvalPlanQualFetchRowMarks as that also seems to be tightly coupled
with HeapTuple.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-10-09 14:32 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-10-09 15:15 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Tom Lane @ 2017-10-09 14:32 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
Alexander Korotkov <a.korotkov@postgrespro.ru> writes:
> For me, it's crucial point that pluggable storages should be able to have
> different MVCC implementation, and correspondingly have full control over
> its interactions with indexes.
> Thus, it would be good if we would get consensus on that point. I'd like
> other discussion participants to comment whether they agree/disagree and
> why.
> Any comments?
TBH, I think that's a good way of ensuring that nothing will ever get
committed. You're trying to draw the storage layer boundary at a point
that will take in most of the system. If we did build it like that,
what we'd end up with would be very reminiscent of mysql's storage
engines, complete with inconsistent behaviors and varying feature sets
across engines. I don't much want to go there.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Re: Pluggable storage Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:32 ` Re: Pluggable storage Tom Lane <tgl@sss.pgh.pa.us>
@ 2017-10-09 15:15 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 0 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-10-09 15:15 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Thomas Munro <thomas.munro@enterprisedb.com>; Amit Kapila <amit.kapila16@gmail.com>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Oct 9, 2017 at 5:32 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Alexander Korotkov <a.korotkov@postgrespro.ru> writes:
> > For me, it's crucial point that pluggable storages should be able to have
> > different MVCC implementation, and correspondingly have full control over
> > its interactions with indexes.
> > Thus, it would be good if we would get consensus on that point. I'd like
> > other discussion participants to comment whether they agree/disagree and
> > why.
> > Any comments?
>
> TBH, I think that's a good way of ensuring that nothing will ever get
> committed. You're trying to draw the storage layer boundary at a point
> that will take in most of the system. If we did build it like that,
> what we'd end up with would be very reminiscent of mysql's storage
> engines, complete with inconsistent behaviors and varying feature sets
> across engines. I don't much want to go there.
>
However, if we insist that pluggable storage should have the same MVCC
implementation, interacts with indexes the same way and also use TIDs as
tuple identifiers, then what useful implementations might we have?
Per-page heap compression and encryption? Or different heap page layout?
Or tuple format? OK, but that doesn't justify such wide API as it's
implemented in the current version of patch in this thread. If we really
want to restrict applicability of pluggable storages that way, then we
probably should give up with "pluggable storages" and make it "pluggable
heap page format" at I proposed upthread.
Implementation of alternative storage would be hard and challenging task.
Yes, it would include reimplementation of significant part of the system.
But that seems inevitable if we're going to implement alternative really
storages (not just hacks over existing storage). And I don't think that
our pluggable storages would be reminiscent of mysql's storage engines
while we're keeping two properties:
1) All the storages use the same WAL stream,
2) All the storages use same transactions and snapshots.
If we keep these two properties, we wouldn't need neither 2PC to run
transactions across different storages, neither separate log for
replication. These two are major drawbacks of MySQL model.
Varying feature sets across engines seems inevitable and natural. We've to
invent alternative storages to have features whose are hard to have in our
current storage. So, no wonder that feature sets would be varying...
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Re: Pluggable storage Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-06-28 12:39 ` Amit Kapila <amit.kapila16@gmail.com>
1 sibling, 0 replies; 138+ messages in thread
From: Amit Kapila @ 2017-06-28 12:39 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Jun 27, 2017 at 7:23 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> On Tue, Jun 27, 2017 at 4:08 PM, Amit Kapila <amit.kapila16@gmail.com>
> wrote:
>>
>> Similarly,
>> if the page format is changed you need to consider all page scan API's
>> like heapgettup_pagemode/heapgetpage.
>
>
> If page format is changed, then heapgettup_pagemode/heapgetpage should use
> appropriate API functions for manipulating page items. I'm very afraid of
> overriding whole heapgettup_pagemode/heapgetpage and monstrous functions
> like heap_update without understanding of clear use-case. It's definitely
> not needed for changing heap page format.
>
Yeah, we might not change them completely, there will always be some
common parts, but as of now, this API considers that we can return a
pointer to tuple on the page with just having a pin on the buffer.
This is based on the assumption that nobody can update the current
tuple, only a new tuple will be created on the update. For some use
cases like in-place updates, we might want to do that differently.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-07-15 02:14 ` Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
1 sibling, 1 reply; 138+ messages in thread
From: Robert Haas @ 2017-07-15 02:14 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Jun 22, 2017 at 9:30 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> If #1 is only about changing tuple and page formats, then could be much
> simpler than the patch upthread? We can implement "page format access
> methods" with routines for insertion, update, pruning and deletion of tuples
> *in particular page*. There is no need to redefine high-level logic for
> scanning heap, doing updates and so on...
That assumes that every tuple format does those things in the same
way, which I suspect is not likely to be the case. I think that
pruning and vacuum are artifacts of the current heap format, and they
may be nonexistent or take some altogether different form in some
other storage engine. InnoDB isn't much like the PostgreSQL heap, and
neither is SQL Server, IIUC. If we're creating a heap format that can
only be different in trivial ways from what we have now, and anything
that really changes the paradigm is off-limits, well, that's not
really interesting enough to justify the work of creating a heap
storage API.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
@ 2017-07-15 22:36 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-07-19 17:53 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
0 siblings, 2 replies; 138+ messages in thread
From: Alexander Korotkov @ 2017-07-15 22:36 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Jul 15, 2017 at 5:14 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Thu, Jun 22, 2017 at 9:30 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > If #1 is only about changing tuple and page formats, then could be much
> > simpler than the patch upthread? We can implement "page format access
> > methods" with routines for insertion, update, pruning and deletion of
> tuples
> > *in particular page*. There is no need to redefine high-level logic for
> > scanning heap, doing updates and so on...
>
> That assumes that every tuple format does those things in the same
> way, which I suspect is not likely to be the case. I think that
> pruning and vacuum are artifacts of the current heap format, and they
> may be nonexistent or take some altogether different form in some
> other storage engine.
I think that pruning and vacuum are artifacts of not only current heap
formats, but they are also artifacts of current index AM API. And this is
more significant circumstance given that we're going to preserve
compatibility of new storage engines with current index AMs. Our current
index AM API assumes that we can delete from index only in bulk manner.
Our payload to index key is TID, not arbitrary piece of data. And that
payload can't be updated.
InnoDB isn't much like the PostgreSQL heap, and
> neither is SQL Server, IIUC. If we're creating a heap format that can
> only be different in trivial ways from what we have now, and anything
> that really changes the paradigm is off-limits, well, that's not
> really interesting enough to justify the work of creating a heap
> storage API.
My concern is that we probably can't do anything that really changes
paradigm while preserving compatibility with index AM API. If you don't
agree with that, it would be good to provide some examples. It seems
unlikely for me that we're going to have something like InnoDB or SQL
Server table with our current index AM API. InnoDB utilizes
index-organized tables where primary and secondary indexes are versioned
independently. SQL Server utilizes flat data structure similar to our
heap, but MVCC implementation also seems very different.
I think in general there are two ways dealing with out index AM API
limitation. One of them is to extend index AM API. At least, we would
need a method for deletion of individual index tuple (for sure, we already
have kill_prior_tuple but it's just a hint for now). Also, it would be
nice to have arbitrary payload to index tuples instead of TID, and method
to update that payload. But that would be quite big amount of work.
Alternatively, we could allow pluggable storages to have their own index
AMs, and that will move this amount of work to the pluggable storage side.
The thing which we would evade is storage API, which would be invasive like
something changing paradigm, but actually allowing just trivial changes in
heap format. Mechanical replacement of heap methods with storage API
methods could lead us there.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-07-16 00:58 ` Peter Geoghegan <pg@bowt.ie>
2017-07-17 10:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-19 17:56 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
1 sibling, 2 replies; 138+ messages in thread
From: Peter Geoghegan @ 2017-07-16 00:58 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sat, Jul 15, 2017 at 3:36 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> I think that pruning and vacuum are artifacts of not only current heap
> formats, but they are also artifacts of current index AM API. And this is
> more significant circumstance given that we're going to preserve
> compatibility of new storage engines with current index AMs. Our current
> index AM API assumes that we can delete from index only in bulk manner. Our
> payload to index key is TID, not arbitrary piece of data. And that payload
> can't be updated.
I agree that this is a big set of problems. This is where I think we
can get the most benefit.
One nice thing about having a fully logical TID is that you don't have
to bloat indexes just because a HOT update was not possible. You bloat
something else instead, certainly, but that can be optimized for
garbage collection. Not all bloat is equal. MVCC based on UNDO can be
very fast because UNDO is very well optimized for garbage collection,
and so can be bloated with no long term consequences, and minor short
term consequences. Still, it isn't fair to blame the Postgres VACUUM
design for the fact that Postgres bloats indexes so easily. Nothing
stops us from adding a new layer of indirection, so that bloating an
index degrades into something that is not even as bad as bloating the
heap [1]. We may just have a data structure problem, which is not
nearly the same thing as a fundamental design problem in the storage
layer.
This approach could pay off soon if we start with unique indexes,
where there is "queue" of row versions that can be pruned with simple
logic, temporal locality helps a lot, only zero or one versions can be
visible to your MVCC snapshot, etc. This might require only minimal
revisions the index AM API, to help nbtree. We could later improve
this so that you bloat UNDO instead of bloating a heap-like structure,
both for indexes and for the actual heap. That seems less urgent.
To repeat myself, for emphasis: *Not all bloat is equal*. Index bloat
makes the way a B-Tree's keyspace is split up far too granular, making
pages sparely packed, a problem that is more or less *irreversible* by
VACUUM or any garbage collection process [2]. That's how B-Trees work
-- they're optimized for concurrency, not for garbage collection.
>> InnoDB isn't much like the PostgreSQL heap, and
>> neither is SQL Server, IIUC. If we're creating a heap format that can
>> only be different in trivial ways from what we have now, and anything
>> that really changes the paradigm is off-limits, well, that's not
>> really interesting enough to justify the work of creating a heap
>> storage API.
>
>
> My concern is that we probably can't do anything that really changes
> paradigm while preserving compatibility with index AM API. If you don't
> agree with that, it would be good to provide some examples. It seems
> unlikely for me that we're going to have something like InnoDB or SQL Server
> table with our current index AM API. InnoDB utilizes index-organized tables
> where primary and secondary indexes are versioned independently. SQL Server
> utilizes flat data structure similar to our heap, but MVCC implementation
> also seems very different.
I strongly agree. I simply don't understand how you can adopt UNDO for
MVCC, and yet expect to get a benefit commensurate with the effort
without also implementing "retail index tuple deletion" first.
Pursuing UNDO this way has the same problem that WARM likely has -- it
doesn't really help with the worst case, where users get big,
unpleasant surprises. Postgres is probably the only major database
system that doesn't support retail index tuple deletion. It's a basic
thing, that has nothing to do with MVCC. Really, what do we have to
lose?
The biggest weakness of the current design is IMV how it fails to
prevent index bloat in the first place, but avoiding bloating index
leaf pages in the first place doesn't seem incompatible with how
VACUUM works. Or at least, let's not assume that it is. We should
avoid throwing the baby out with the bathwater.
> I think in general there are two ways dealing with out index AM API
> limitation. One of them is to extend index AM API. At least, we would need
> a method for deletion of individual index tuple (for sure, we already have
> kill_prior_tuple but it's just a hint for now).
kill_prior_tuple can work well, but, like HOT, it works
inconsistently, in a way that is hard to predict.
> Also, it would be nice to
> have arbitrary payload to index tuples instead of TID, and method to update
> that payload. But that would be quite big amount of work. Alternatively,
> we could allow pluggable storages to have their own index AMs, and that will
> move this amount of work to the pluggable storage side.
I agree with Robert that being able to store an arbitrary payload as a
TID is probably not going to ever work very well. However, I don't
think that that's a reason to give up on the underlying idea: creating
a new layer of indirection for secondary indexes, that allows updates
that now have to create new index tuples to instead just update the
indirection layer metadata.
You can create a mapping of a TID-like 6 byte integer to a primary key
value. Techniques exist. That seems a lot more practical. Of course,
TID is what is sometimes called a "physiological" identifier -- it has
a "physical" component (block number) and "logical" component (item
offset). Nothing I can think of prevents us from creating an
alternative, entirely logical identifier that fits in the same 6
bytes. It can map to a versioning indirection layer, for unique
indexes, or to a primary key value, for secondary indirect indexes.
[1] postgr.es/m/CAH2-Wzmf6intNY1ggiNzOziiO5Eq=DsXfeptODGxO=2j-i1NGQ@mail.gmail.com
[2] https://wiki.postgresql.org/wiki/Key_normalization#VACUUM_and_nbtree_page_deletion
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
@ 2017-07-17 10:22 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 16:51 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
1 sibling, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-07-17 10:22 UTC (permalink / raw)
To: Peter Geoghegan <pg@bowt.ie>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sun, Jul 16, 2017 at 3:58 AM, Peter Geoghegan <pg@bowt.ie> wrote:
> I strongly agree. I simply don't understand how you can adopt UNDO for
> MVCC, and yet expect to get a benefit commensurate with the effort
> without also implementing "retail index tuple deletion" first.
> Pursuing UNDO this way has the same problem that WARM likely has -- it
> doesn't really help with the worst case, where users get big,
> unpleasant surprises. Postgres is probably the only major database
> system that doesn't support retail index tuple deletion. It's a basic
> thing, that has nothing to do with MVCC. Really, what do we have to
> lose?
>
I think that "retail index tuple deletion" is the feature which could give
us some advantages even independently from pluggable storages. For
example, imagine very large table with only small amount of dead tuples.
In this case, it would be cheaper to delete index links to those dead
tuples one by one using "retail index tuple deletion", rather than do full
scan of every index to perform "bulk delete" of index tuples. One may
argue that you shouldn't do vacuum of large table when only small amount of
tuples are dead. But in terms of index bloat mitigation, very aggressive
vacuum strategy could be justified.
I agree with Robert that being able to store an arbitrary payload as a
> TID is probably not going to ever work very well.
Support of arbitrary payload as a TID doesn't sound easy. However, that
doesn't mean it's unachievable. For me, it's more like long way which could
be traveled step by step. Some of our existing index access methods
(B-tree, hash, GiST, SP-GiST) may support arbitrary payload relatively
easy, because they are not relying on its internal structure. For others
(GIN, BRIN) arbitrary payload is much harder to support, but I wouldn't say
it's impossible. However, if we make arbitrary payload support an option
of index AM and implement this support for first group of index AMs, it
would be already great step forward. So, for sample, it would be possible
to use indirect indexes when primary key is not 6-bytes, if index AM
supports arbitrary payload.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-07-17 10:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-07-17 16:51 ` Peter Geoghegan <pg@bowt.ie>
2017-07-17 20:24 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
0 siblings, 1 reply; 138+ messages in thread
From: Peter Geoghegan @ 2017-07-17 16:51 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Jul 17, 2017 at 3:22 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> I think that "retail index tuple deletion" is the feature which could give
> us some advantages even independently from pluggable storages. For example,
> imagine very large table with only small amount of dead tuples. In this
> case, it would be cheaper to delete index links to those dead tuples one by
> one using "retail index tuple deletion", rather than do full scan of every
> index to perform "bulk delete" of index tuples. One may argue that you
> shouldn't do vacuum of large table when only small amount of tuples are
> dead. But in terms of index bloat mitigation, very aggressive vacuum
> strategy could be justified.
Yes, definitely. Especially with the visibility map. Even still, I
tend to think that for unique indexes, true duplicates should be
disallowed, and dealt with with an additional layer of indirection. So
this would be for secondary indexes.
>> I agree with Robert that being able to store an arbitrary payload as a
>> TID is probably not going to ever work very well.
>
>
> Support of arbitrary payload as a TID doesn't sound easy. However, that
> doesn't mean it's unachievable. For me, it's more like long way which could
> be traveled step by step.
To be fair, it probably is achievable. Where there is a will, there is
a way. I just think that it will be easier to find a different way of
realizing similar benefits. I'm mostly talking about benefits around
making it cheap to have many secondary indexes by having logical
indirection instead of physical pointers (doesn't *have* to be
user-visible primary key values). HOT simply isn't effective enough at
preventing UPDATE index tuple insertions for indexes on unchanged
attributes, often just because pruning can fail to happen in time,
which WARM will not fix.
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-07-17 10:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 16:51 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
@ 2017-07-17 20:24 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 23:34 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
0 siblings, 1 reply; 138+ messages in thread
From: Alexander Korotkov @ 2017-07-17 20:24 UTC (permalink / raw)
To: Peter Geoghegan <pg@bowt.ie>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Jul 17, 2017 at 7:51 PM, Peter Geoghegan <pg@bowt.ie> wrote:
> On Mon, Jul 17, 2017 at 3:22 AM, Alexander Korotkov
> <a.korotkov@postgrespro.ru> wrote:
> > I think that "retail index tuple deletion" is the feature which could
> give
> > us some advantages even independently from pluggable storages. For
> example,
> > imagine very large table with only small amount of dead tuples. In this
> > case, it would be cheaper to delete index links to those dead tuples one
> by
> > one using "retail index tuple deletion", rather than do full scan of
> every
> > index to perform "bulk delete" of index tuples. One may argue that you
> > shouldn't do vacuum of large table when only small amount of tuples are
> > dead. But in terms of index bloat mitigation, very aggressive vacuum
> > strategy could be justified.
>
> Yes, definitely. Especially with the visibility map. Even still, I
> tend to think that for unique indexes, true duplicates should be
> disallowed, and dealt with with an additional layer of indirection. So
> this would be for secondary indexes.
>
It's probably depends on particular storage (once we have pluggable
storages). Some storages would have additional level of indirection while
others wouldn't. But even if unique index contain no true duplicates, it's
still possible that true delete happen. Then we still have to delete tuple
even from unique index.
>> I agree with Robert that being able to store an arbitrary payload as a
> >> TID is probably not going to ever work very well.
> >
> >
> > Support of arbitrary payload as a TID doesn't sound easy. However, that
> > doesn't mean it's unachievable. For me, it's more like long way which
> could
> > be traveled step by step.
>
> To be fair, it probably is achievable. Where there is a will, there is
> a way. I just think that it will be easier to find a different way of
> realizing similar benefits. I'm mostly talking about benefits around
> making it cheap to have many secondary indexes by having logical
> indirection instead of physical pointers (doesn't *have* to be
> user-visible primary key values).
It's possible to add indirection layer "on demand". Thus, initially index
tuples point directly to the heap tuple. If tuple gets updates and doesn't
fit to the page anymore, then it's moved to another place with redirect in
the old place. I think that if carefully designed, it's possible to
guarantee there is at most one redirect.
But I sill think that evading arbitrary payload for indexes is delaying of
inevitable, if only we want pluggable storages and want them to reuse
existing index AMs. So, for example, arbitrary payload together with
ability to update this payload allows us to make indexes separately
versioned (have separate garbage collection process more or less unrelated
to heap). Despite overhead caused by MVCC attributes, I think such indexes
could give significant advantages in various workloads.
> HOT simply isn't effective enough at
> preventing UPDATE index tuple insertions for indexes on unchanged
> attributes, often just because pruning can fail to happen in time,
> which WARM will not fix.
>
Right. I think HOT and WARM depend on factors which are hard to control:
distribution of UPDATEs between heap pages, oldest snapshot and so on.
It's quite hard for DBA to understand why table starts getting bloat while
it didn't before.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 138+ messages in thread
* Re: Pluggable storage
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-24 18:22 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Re: Pluggable storage Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Re: Pluggable storage Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:30 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-15 02:14 ` Re: Pluggable storage Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-07-17 10:22 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 16:51 ` Re: Pluggable storage Peter Geoghegan <pg@bowt.ie>
2017-07-17 20:24 ` Re: Pluggable storage Alexander Korotkov <a.korotkov@postgrespro.ru>
@ 2017-07-17 23:34 ` Peter Geoghegan <pg@bowt.ie>
0 siblings, 0 replies; 138+ messages in thread
From: Peter Geoghegan @ 2017-07-17 23:34 UTC (permalink / raw)
To: Alexander Korotkov <a.korotkov@postgrespro.ru>; +Cc: Robert Haas <robertmhaas@gmail.com>; Haribabu Kommi <kommi.haribabu@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Mon, Jul 17, 2017 at 1:24 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
> It's probably depends on particular storage (once we have pluggable
> storages). Some storages would have additional level of indirection while
> others wouldn't.
Agreed. Like kill_prior_tuple, it's an optional capability, and where
implemented is implemented in a fairly consistent way.
> But even if unique index contain no true duplicates, it's
> still possible that true delete happen. Then we still have to delete tuple
> even from unique index.
I think I agree. I've been looking over the ARIES paper [1] again
today. They say this:
"For index updates, in the interest of increasing concurrency, we do
not want to prevent the space released by one transaction from being
consumed by another before the commit of the first transaction."
You can literally reclaim space from an index tuple deletion
*immediately* with their design, which matters because you want to
reclaim space as early as possible, before a page spit is needed.
Obviously they understand how important this is.
This might not work so well with an MVCC system, where there are no
2PL predicate locks. You need to keep a "ghost record", even for
non-unique indexes, and the deletion can only happen when the xact
commits. Remember, the block number cannot be used to see if there was
changes against the page, unlike the heap, because you have to worry
about page splits and page merges/deletion. UNDO is entirely logical
for indexes for this reason. (This is why UNDO does not actually undo
page splits, relation extension, etc. Only REDO/WAL always works at
the level of individual pages in all cases. UNDO for MVCC is not as
different to our design as I once thought.).
The reason I want to at least start with unique indexes is because you
need a TID to make non-unique/secondary indexes have unique keys
(unique keys are always needed if retail index tuple insertion is
always supported). For unique indexes, you really can do an update in
the index (see my design below for one example of how that can work),
but I think you need something more like a deletion followed by an
insertion for non-unique indexes, because there the physical/heap TID
changed, and that's part of the key, and that might belong on a
different page. You therefore haven't really fixed the problem with
secondary indexes sometimes needing new index tuples even though user
visible attributes weren't updated.
You haven't fixed the problem with secondary index, unless, of course,
all secondary indexes have logical pointers to begin with, such as the
PK value. Then you only need to "insert and delete, not update" when
the PK value is updated or when a secondary index needs a new index
tuple with distinct user visible attribute values to the previous
version's -- you fix the secondary index problem. And, while your
"version chain overflow indirection" structure is basically something
that lives outside the heap, it is still only needed for one index,
and not all of them.
This new indirection structure is a really nice target for pruning,
because you can prune physical TIDs that no possible snapshot could
use, unlike with the heap, where EvalPlanQual() could make any heap
tuple visible to snapshots at or after the minimal snapshot horizon
implied by RecentGlobalXmin. And, because index scans on any index can
prune for everyone.
You could also do "true index deletes", as you suggest, but you'd need
to have ghost records there too, and you'd need an asynchronous
cleanup process to do the cleanup when the deleting xact committed.
I'm not sure if it's worth doing that eagerly. It may or may not be
better to hope for kill_prior_tuple to do the job for us. Not sure
where this leaves index-only scans on secondary indexes..."true index
deletes" might be justified by making index only scans work more often
in general, especially for secondary indexes with logical pointers.
I'm starting to think that you were right all along about indirect
indexes needing to store PK values. Perhaps we should just bite the
bullet...it's not like places like the bufpage.c index routines
actually know or care about whether or not the index tuple has a TID,
what a TID is, etc. They care about stuff like the header values of
index tuples, and the page ItemId array, but TID is, as you put it,
merely payload.
> It's possible to add indirection layer "on demand". Thus, initially index
> tuples point directly to the heap tuple. If tuple gets updates and doesn't
> fit to the page anymore, then it's moved to another place with redirect in
> the old place. I think that if carefully designed, it's possible to
> guarantee there is at most one redirect.
This is actually what I was thinking. Here is a sketch:
When you start out, index tuples in nbtree are the same as today --
one physical pointer (TID). But, on the first update to a PK index,
they grow a new pointer, but this is not a physical/heap TID. It's a
pointer to some kind of indirection structure that manages version
chains. You end up with an index with almost exactly the same external
interface as today, with one difference: you tell nbtree if something
is an insert or update, at least for unique indexes. Of course, you
need to have something to update in the index if it's an update, and
nbtree needs to be informed what that is.
My first guess is that we should limit the number of TIDs to two in
all cases, and start with only one physical TID, because:
* The first TID can always be the latest version, which in practice is
all most snapshots care about.
* We want to sharply limit the worst case page bloat, because
otherwise you have the same basic problem. Some queries might be a bit
slower, but it's worth it to be confident that bloat can only get so
bad.
* Simpler "1/3 of a page" enforcement. We simply add
"sizeof(ItemPointerData)" to the calculation.
* Gray says that split points are sometimes better if they're the
average of the min and max keys on the page, rather than the point at
which each half gets the most even share of space. Big index tuples
are basically bad for this.
> But I sill think that evading arbitrary payload for indexes is delaying of
> inevitable, if only we want pluggable storages and want them to reuse
> existing index AMs. So, for example, arbitrary payload together with
> ability to update this payload allows us to make indexes separately
> versioned (have separate garbage collection process more or less unrelated
> to heap). Despite overhead caused by MVCC attributes, I think such indexes
> could give significant advantages in various workloads.
Yeah. Technically you could have some indirection to keep under 6
bytes when that isn't assured by the PK index tuple width, but it
probably wouldn't be worth it. TID is almost like just another
attribute. The more I look, the less I think that TID is this thing
that a bunch of code makes many assumptions about that we will never
find all of. *Plenty* of TIDs today do not point to the heap at all.
For example, internal pages in nbtree uses TIDs that point to the
level below.
You would break some code within indextuple.c, but that doesn't seem
so bad. IndexInfoFindDataOffset() already has to deal with
variable-width NULL bitmaps. Why not a variabke-length pointer, too?
[1] https://pdfs.semanticscholar.org/39e3/d058a5987cb643e000bce555676d71be1c80.pdf
--
Peter Geoghegan
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 138+ messages in thread
26 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.
end of thread, other threads:[~2018-06-22 04:24 UTC | newest]
Thread overview: 138+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2016-08-12 23:15 Pluggable storage Alvaro Herrera <alvherre@2ndQuadrant.com>
2016-08-15 11:55 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2016-08-15 16:02 ` Robert Haas <robertmhaas@gmail.com>
2016-08-16 18:46 ` Andres Freund <andres@anarazel.de>
2016-08-18 07:58 ` Simon Riggs <simon@2ndquadrant.com>
2016-08-18 11:01 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2016-08-18 16:24 ` Andres Freund <andres@anarazel.de>
2016-08-18 14:44 ` Ants Aasma <ants.aasma@eesti.ee>
2016-08-18 15:14 ` Andres Freund <andres@anarazel.de>
2016-08-15 18:09 ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-17 13:01 ` Anastasia Lubennikova <a.lubennikova@postgrespro.ru>
2016-08-17 17:03 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2016-08-18 11:23 ` Amit Kapila <amit.kapila16@gmail.com>
2016-08-24 18:22 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2016-10-13 20:26 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-13 01:50 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-21 19:47 ` Robert Haas <robertmhaas@gmail.com>
2017-06-22 01:01 ` Michael Paquier <michael.paquier@gmail.com>
2017-06-22 02:12 ` Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
2017-06-22 03:00 ` Michael Paquier <michael.paquier@gmail.com>
2017-06-22 12:32 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:27 ` Robert Haas <robertmhaas@gmail.com>
2017-06-22 14:36 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 21:36 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-22 23:49 ` Michael Paquier <michael.paquier@gmail.com>
2017-06-23 08:38 ` Teodor Sigaev <teodor@sigaev.ru>
2017-06-23 14:07 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-23 14:24 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-06-25 09:38 ` Julien Rouhaud <julien.rouhaud@dalibo.com>
2017-06-26 15:18 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-26 19:55 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-06-26 20:39 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-22 14:30 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:08 ` Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 13:53 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 03:16 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-14 13:35 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-15 02:30 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 06:03 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-07-23 06:10 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-01 08:26 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-07 13:12 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:01 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:17 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-21 07:28 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-21 09:25 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-23 05:35 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-23 13:59 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-26 03:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-01 01:51 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-07 01:53 ` Thomas Munro <thomas.munro@enterprisedb.com>
2017-09-09 03:23 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-12 05:52 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 05:17 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-14 19:10 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-19 07:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 16:51 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-09 14:22 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-11 20:08 ` Robert Haas <robertmhaas@gmail.com>
2017-10-11 20:54 ` Peter Geoghegan <pg@bowt.ie>
2017-10-12 20:38 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-12 21:23 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 00:00 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 00:55 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 08:28 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-10-13 09:25 ` Kuntal Ghosh <kuntalghosh.2007@gmail.com>
2017-10-13 18:37 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 18:54 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-20 03:47 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-25 06:07 ` Robert Haas <robertmhaas@gmail.com>
2017-10-25 11:59 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-27 05:06 ` Robert Haas <robertmhaas@gmail.com>
2017-10-31 09:59 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-07 09:34 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-11-14 08:09 ` Michael Paquier <michael.paquier@gmail.com>
2017-11-29 04:50 ` Michael Paquier <michael.paquier@gmail.com>
2017-12-12 04:06 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 03:54 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-12-27 12:33 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-03 07:08 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-03 23:00 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 05:03 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-04 22:55 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-04 23:20 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-01-05 16:20 ` Robert Haas <robertmhaas@gmail.com>
2018-01-05 19:31 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-01-09 12:42 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-05 11:22 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-16 10:56 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-02-21 00:29 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-02-22 23:20 ` Robert Haas <robertmhaas@gmail.com>
2018-02-26 08:19 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2018-03-28 16:23 ` David Steele <david@pgmasters.net>
2018-03-29 00:39 ` Michael Paquier <michael@paquier.xyz>
2018-03-29 05:54 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-04-20 06:44 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-13 20:20 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-06-14 02:25 ` Amit Kapila <amit.kapila16@gmail.com>
2018-06-18 19:43 ` AJG <ayden@gera.co.nz>
2018-06-18 20:14 ` Andres Freund <andres@anarazel.de>
2018-06-19 03:59 ` Amit Kapila <amit.kapila16@gmail.com>
2018-06-22 04:24 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2018-03-29 13:07 ` David Steele <david@pgmasters.net>
2017-11-14 10:42 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2017-11-14 13:17 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-13 17:59 ` Peter Geoghegan <pg@bowt.ie>
2017-10-13 18:41 ` Robert Haas <robertmhaas@gmail.com>
2017-10-13 19:39 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-10-20 03:56 ` Amit Kapila <amit.kapila16@gmail.com>
2017-10-09 14:32 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-10-09 15:15 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 12:39 ` Amit Kapila <amit.kapila16@gmail.com>
2017-07-15 02:14 ` Robert Haas <robertmhaas@gmail.com>
2017-07-15 22:36 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-16 00:58 ` Peter Geoghegan <pg@bowt.ie>
2017-07-17 10:22 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 16:51 ` Peter Geoghegan <pg@bowt.ie>
2017-07-17 20:24 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-07-17 23:34 ` Peter Geoghegan <pg@bowt.ie>
2017-07-19 17:56 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 18:53 ` Peter Geoghegan <pg@bowt.ie>
2017-07-19 17:53 ` Robert Haas <robertmhaas@gmail.com>
2017-06-22 21:27 ` Tomas Vondra <tomas.vondra@2ndquadrant.com>
2017-07-15 02:27 ` Robert Haas <robertmhaas@gmail.com>
2017-06-28 02:07 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-22 12:16 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-27 13:19 ` Amit Kapila <amit.kapila16@gmail.com>
2017-06-27 14:00 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-06-28 02:13 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-06-28 12:50 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-08 04:21 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-12 05:04 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-13 07:21 ` Amit Kapila <amit.kapila16@gmail.com>
2017-08-13 13:23 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-08-15 06:53 ` Andres Freund <andres@anarazel.de>
2017-08-23 05:26 ` Haribabu Kommi <kommi.haribabu@gmail.com>
2017-09-27 12:08 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
2017-09-27 12:05 ` Alexander Korotkov <a.korotkov@postgrespro.ru>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox