nested xacts and phantom Xids
Ok people,
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.
Please review, test and comment.
Missing: rollback of SET CONSTRAINTS and GUC vars.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
Y una voz del caos me habl� y me dijo
"Sonr�e y s� feliz, podr�a ser peor".
Y sonre�. Y fui feliz.
Y fue peor.
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.
I looked at the phantom XIDs stuff a bit. I still have little confidence
that the concept is correct :-( but here are some comments on the code
level.
+ * They are marked immediately in pg_subtrans as direct childs of the topmost + * Xid (no matter how deep we are in the transaction tree),
Why is that a good idea --- won't it cause problems when you
commit/abort an intermediate level of subtransaction?
+ * All this happens when HeapTupleHeaderSetXmax is called and the Xmin of the + * tuple is one of the Xids of the current transaction. + * + * Note that HeapTupleHeaderGetXmax/GetXmin return the masqueraded Xmin and + * Xmax, not the real one in the tuple header.
I think that putting this sort of logic into Set/Get Xmin/Xmax is a
horrid idea. They are supposed to be transparent fetch/store macros,
not arbitrarily-complex filter functions. They're also supposed to
be reasonably fast :-(
+ * ... We know to do this when SetXmax is called + * on a tuple that has the HEAP_MARKED_FOR_UPDATE bit set.
Uglier and uglier.
***************
*** 267,274 ****
return true;/* deleting subtransaction aborted */
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! return true;Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
--- 268,276 ---- return true;/* deleting subtransaction aborted */
! if (tuple->t_infomask & HEAP_XMIN_IS_PHANTOM)
! if (TransactionPhantomIsCommittedPhantom(HeapTupleHeaderGetPhantomId(tuple)))
! return true;Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
Er, what happened to checking for the "deleting subtransaction aborted"
case?
On the whole, I think this was an interesting exercise but also an utter
failure. We'd be far better off to eat the extra 4 bytes per tuple
header and put back the separate Xmax field. The costs in both runtime
and complexity/reliability of this patch are simply not justified by
a 4-byte savings.
regards, tom lane
Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.I looked at the phantom XIDs stuff a bit. I still have little confidence
that the concept is correct :-( but here are some comments on the code
level.+ * They are marked immediately in pg_subtrans as direct childs of the topmost + * Xid (no matter how deep we are in the transaction tree),Why is that a good idea --- won't it cause problems when you
commit/abort an intermediate level of subtransaction?
I don't think so. The phantom xid is used only by the outside
transaction waiting for that tuple to be committe or aborted. The
outside tranaction will sleep on the topmost xid completing, then check
the phantom xid status for commit/abort. Within the transaction, I think
he uses command counter to know the creation and destruction sub-xid.
I think right now phantom xids are used only for making parts of a
subtransaction committed or aborted and only in cases where the tuple is
created and destroyed by parts of the same transaction tree.
I don't feel too bad about the runtime cost if only subtransactions are
paying that cost. I know we are really stretching the system here but I
would like to try a little more rather than give up and taking a space
hit for all tuples.
---------------------------------------------------------------------------
+ * All this happens when HeapTupleHeaderSetXmax is called and the Xmin of the + * tuple is one of the Xids of the current transaction. + * + * Note that HeapTupleHeaderGetXmax/GetXmin return the masqueraded Xmin and + * Xmax, not the real one in the tuple header.I think that putting this sort of logic into Set/Get Xmin/Xmax is a
horrid idea. They are supposed to be transparent fetch/store macros,
not arbitrarily-complex filter functions. They're also supposed to
be reasonably fast :-(+ * ... We know to do this when SetXmax is called + * on a tuple that has the HEAP_MARKED_FOR_UPDATE bit set.Uglier and uglier.
***************
*** 267,274 ****
return true;/* deleting subtransaction aborted */
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! return true;Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
--- 268,276 ---- return true;/* deleting subtransaction aborted */
! if (tuple->t_infomask & HEAP_XMIN_IS_PHANTOM)
! if (TransactionPhantomIsCommittedPhantom(HeapTupleHeaderGetPhantomId(tuple)))
! return true;Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
Er, what happened to checking for the "deleting subtransaction aborted"
case?On the whole, I think this was an interesting exercise but also an utter
failure. We'd be far better off to eat the extra 4 bytes per tuple
header and put back the separate Xmax field. The costs in both runtime
and complexity/reliability of this patch are simply not justified by
a 4-byte savings.regards, tom lane
---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
Bruce Momjian wrote:
Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.I looked at the phantom XIDs stuff a bit. I still have little confidence
that the concept is correct :-( but here are some comments on the code
level.+ * They are marked immediately in pg_subtrans as direct childs of the topmost + * Xid (no matter how deep we are in the transaction tree),Why is that a good idea --- won't it cause problems when you
commit/abort an intermediate level of subtransaction?I don't think so. The phantom xid is used only by the outside
transaction waiting for that tuple to be committe or aborted. The
outside tranaction will sleep on the topmost xid completing, then check
the phantom xid status for commit/abort. Within the transaction, I think
he uses command counter to know the creation and destruction sub-xid.I think right now phantom xids are used only for making parts of a
subtransaction committed or aborted and only in cases where the tuple is
created and destroyed by parts of the same transaction tree.I don't feel too bad about the runtime cost if only subtransactions are
paying that cost. I know we are really stretching the system here but I
would like to try a little more rather than give up and taking a space
hit for all tuples.
Let me add that outside transactions read those phantom xid only when
they are doing dirty reads. What I don't understand is when do outside
transactions see tuples created inside a transaction? INSERT into a
table with a unique key?
Once the main transaction commits, these phantom tuples should work just
like ordinary tuples except they get their cmin overwritten when they
are expired, I think.
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
Bruce Momjian <pgman@candle.pha.pa.us> writes:
I don't feel too bad about the runtime cost if only subtransactions are
paying that cost.
That's exactly why I'm so exercised about what's been done to the
HeapTupleSet/Get macros. That's significant cost that's paid even when
you're not using *any* of this stuff.
I know we are really stretching the system here but I
would like to try a little more rather than give up and taking a space
hit for all tuples.
I don't even have any confidence that there are no fundamental bugs
in the phantom-xid concept :-(. I'd be willing to play along if an
implementation that seemed acceptable speedwise were being offered,
but this thing is not preferable to four-more-bytes even if it works.
regards, tom lane
Bruce Momjian <pgman@candle.pha.pa.us> writes:
Let me add that outside transactions read those phantom xid only when
they are doing dirty reads. What I don't understand is when do outside
transactions see tuples created inside a transaction? INSERT into a
table with a unique key?
Once the main transaction commits, these phantom tuples should work just
like ordinary tuples except they get their cmin overwritten when they
are expired, I think.
You're not doing anything to increase my confidence level in this
concept. You invented it, and even you aren't sure how it works.
regards, tom lane
Tom Lane wrote:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
Let me add that outside transactions read those phantom xid only when
they are doing dirty reads. What I don't understand is when do outside
transactions see tuples created inside a transaction? INSERT into a
table with a unique key?Once the main transaction commits, these phantom tuples should work just
like ordinary tuples except they get their cmin overwritten when they
are expired, I think.You're not doing anything to increase my confidence level in this
concept. You invented it, and even you aren't sure how it works.
And your point is? I am trying to help Alvaro. I came up with an
idea, and some thought it would help solve the problem. What ideas do
you have to help him?
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.
I took a very preliminary look through the nested-xacts patch, too.
Missing: rollback of SET CONSTRAINTS and GUC vars.
There's a good deal more than that missing :-(. Here are the modules or
actions that are called in CommitTransaction and/or AbortTransaction
that have not yet been touched by the patch:
shared-inval message queue processing (inval.c)
lo_commit
localbuf.c (refcounts need fixed same as bufmgr)
eoxactcallbacks API needs extension
GUC
namespace (temp namespace cleanup)
files (close/drop temp files)
reset current userid during xact abort
password file updating
SetReindexProcessing disable (can these be nested??)
Of these the one that I think most urgently needs attention is inval.c;
that is complicated code already and figuring out what should happen
for subtrans commit/abort is not trivial. The others look relatively
straightforward to fix, if tedious.
Given patches for inval.c and guc.c, I would say that the patch is
functionally close enough to done that we could commit to including
it in 7.5 --- the other stuff could be wrapped up post-feature-freeze.
BUT (you knew there was a but coming, right?) ... I am really really
disturbed about the potential performance hit from this patch.
I don't think we want to have a nested-transaction feature that imposes
significant overhead on people who aren't actually using nested
transactions. As I looked through the patch I found quite a few places
that would impose such overhead.
The main thing that's bothering me is that TransactionIdIsInProgress
has been turned from a relatively cheap test (just scan the in-memory
PGPROC array) into an exceedingly expensive one. It's gratutitously
badly coded (for N open main transactions it does N searches of the
subtrans tree, when one would do), but I don't really want it going to
the subtrans tree at all during typical cases. We had talked about
keeping a limited number of active subtrans IDs in the PGPROC array,
and I think we're really going to need to do that to buy back
performance here.
(BTW, what is the intended behavior of TransactionIdIsInProgress for
a committed or aborted subtransaction whose parent is still open?
If it has to recognize aborted subtranses as still in progress then
things get very messy, but I think it probably doesn't have to.)
I'm quite unhappy with the wide use of SubTransXidsHaveCommonAncestor to
replace TransactionIdEquals in tqual.c, too. This turns very cheap tests
into very expensive ones --- whether or not you use subtransactions ---
and unfortunately tqual.c is coded on the assumption that these tests
are cheap. We can *not* afford to repeat SubTransXidsHaveCommonAncestor
for every open main transaction every time we check a tuple. In many
ways this is the same problem as with TransactionIdIsInProgress, and
it probably needs a similar solution.
Also I found it annoying that XactLockTableWait waits for a top xact
even when target subxact has already aborted; possibly this is even a
recipe for deadlock. Perhaps should iterate up the tree one level at a
time, waiting for each? Would mean that subxacts have to acquire a
lock on their xid that other people can wait for, but this is not
necessarily bad. (In fact, if we do that then I don't think
XactLockTableWait has to change at all.)
So I think there are several must-fix performance issues as well before
we can make a decision to accept this for 7.5.
I'd feel better about this if we had another month, but with ten days
before feature freeze it's gonna be touch and go.
regards, tom lane
Bruce Momjian <pgman@candle.pha.pa.us> writes:
What ideas do you have to help him?
Eat the four-byte overhead.
Quite frankly, given the other functionality and performance issues
he has to solve in the next ten days (see my other message just now),
I think he'd be a fool to spend one more minute on phantom XIDs.
Maybe for 7.6 someone will have the time to make the idea work.
regards, tom lane
Tom Lane wrote:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
What ideas do you have to help him?
Eat the four-byte overhead.
Quite frankly, given the other functionality and performance issues
he has to solve in the next ten days (see my other message just now),
I think he'd be a fool to spend one more minute on phantom XIDs.
Maybe for 7.6 someone will have the time to make the idea work.
Well, the problem with eating the 4-byte overhead is that it has a
performance/storage impact for all installations, not just those that
use nested transactions. You were discussing a performance impact for
nested transactions, but I assume that is an impact when some on the
server are using nested transactions and others are not, while this hit
would be for all sites, even when no one is using nested transactions.
However, given the list you just supplied, I agree Alvaro should focus
on these items and add the 4-bytes to store needed values rather than
continue to hack on phantom xids. We can certainly revisit that later,
even in 7.6, or never. We can put Manfred on it. He did the compaction
in the first place. :-)
One point is that if we want to re-add those 4 bytes, we have to get
community buy-in for it because it is a step backward for those who are
not going to use nested transactions. (I don't want to go the
compile-time switch route.)
As far as cleaning up some of the items after feature freeze, well, I
would like to avoid that, but it seems safe to do because the changes
should be very localized, and because win32 will be cleaning up things
during feature freeze too, I am sure.
However, going into feature freeze knowing a features isn't 100%
functional is something we try to avoid, so this would have to be a
special exception based on the fact that the features is very difficult,
and that Alvaro is working full-time on this. It does take us down the
slippery slope of having the release process dictated by completion of
nested transactions.
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
On Sun, Jun 20, 2004 at 04:37:16PM -0400, Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.I looked at the phantom XIDs stuff a bit. I still have little confidence
that the concept is correct :-( but here are some comments on the code
level.
Ok. I for one think this got much more complex than I had originally
thought it would be. I agree the changes to Set/Get Xmin/Xmax are way
beyond what one would want, but the alternative would be to spread the
complexity into their callers and I think that would be much worse.
I don't have a lot of confidence in this either. The patch will be
available in archives if anybody wants to implement this in a cleaner
and safer way; I'll continue working on the rest of the things you
pointed out in the subtransactions patch.
Sadly, something broke in a really strange way between my last cvs
update and today's, because I can't get the patch to initdb. It
compiles without warnings (and I did make distclean), but there's a
weird bug I'll have to pursue.
Regarding the invalidation messages, what I'm currently looking at is to
add a TransactionId to each message, which will be CurrentTransactionId
for each new message. When a subxact commits, all its messages are
relabeled to its parent. When a subxact aborts, all messages labeled
with its TransactionId are locally processed and discarded. This is
tricky because chunks are prepended to the queue, but it also means we
can stop processing as soon as a message belongs to another transaction.
I think GUC vars are easier: when a variable is about to change value
inside a subtransaction, save the previous value in a list from which it
will be restored if the subtransaction later aborts.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"Hay dos momentos en la vida de un hombre en los que no deber�a
especular: cuando puede permit�rselo y cuando no puede" (Mark Twain)
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Regarding the invalidation messages, what I'm currently looking at is to
add a TransactionId to each message, which will be CurrentTransactionId
for each new message. When a subxact commits, all its messages are
relabeled to its parent. When a subxact aborts, all messages labeled
with its TransactionId are locally processed and discarded.
Sounds plausible offhand.
This is tricky because chunks are prepended to the queue, but it also
means we can stop processing as soon as a message belongs to another
transaction.
AFAIR there isn't any essential semantics to the ordering of the queue
entries, so you can probably get away with reordering if that makes life
any easier.
Also, rather than labeling each entry individually, it might be better
to keep a separate list for each level of transaction. Then instead of
relabeling, you'd just concat the subtrans list to its parent's. Seems
like this should be faster and less storage.
regards, tom lane
On Mon, Jun 21, 2004 at 10:28:59PM -0400, Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
This is tricky because chunks are prepended to the queue, but it also
means we can stop processing as soon as a message belongs to another
transaction.AFAIR there isn't any essential semantics to the ordering of the queue
entries, so you can probably get away with reordering if that makes life
any easier.Also, rather than labeling each entry individually, it might be better
to keep a separate list for each level of transaction. Then instead of
relabeling, you'd just concat the subtrans list to its parent's. Seems
like this should be faster and less storage.
Right, but it makes harder to detect when a duplicate relcache entry is
going to be inserted. Judging from the commentary in the file this is
an issue.
Another idea I had was:
1. at subtransaction start, add a special "boundary" message carrying
the new Xid.
2. at subtransaction abort, process the first chunk backwards until the
boundary message with the corresponding Xid is found; if it's not, jump
to the next and repeat.
At subtransaction commit nothing special needs to be done.
This avoids having to label each message and to change the message
appending scheme.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"The eagle never lost so much time, as
when he submitted to learn of the crow." (William Blake)
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
On Mon, Jun 21, 2004 at 10:28:59PM -0400, Tom Lane wrote:
Also, rather than labeling each entry individually, it might be better
to keep a separate list for each level of transaction. Then instead of
relabeling, you'd just concat the subtrans list to its parent's. Seems
like this should be faster and less storage.
Right, but it makes harder to detect when a duplicate relcache entry is
going to be inserted. Judging from the commentary in the file this is
an issue.
It's only a minor efficiency hack; don't get too tense about avoiding dups.
(We don't bother to check for dup catcache-inval entries at all...)
The only thing I'd suggest you need to preserve is the business about
invalidating catcache before relcache; again that's just an efficiency
hack, but it seems simple enough to be worth doing.
regards, tom lane
On Sun, Jun 20, 2004 at 08:49:22PM -0400, Tom Lane wrote:
Of these the one that I think most urgently needs attention is inval.c;
that is complicated code already and figuring out what should happen
for subtrans commit/abort is not trivial. The others look relatively
straightforward to fix, if tedious.
A patch for this is attached. It _seems_ to work ok; conceptually it
seems ok too. I have done some testing which tends to indicate that it
is right, but I'm not sure of all the implications this code has so I
don't know how to test it exhaustively. Of course, regression tests
pass, but this doesn't actually prove anything.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"�Que diferencia tiene para los muertos, los hu�rfanos, y aquellos que han
perdido su hogar, si la loca destrucci�n ha sido realizada bajo el nombre
del totalitarismo o del santo nombre de la libertad y la democracia?" (Gandhi)
Attachments:
inval.patchtext/plain; charset=us-asciiDownload
Index: src/include/storage/sinval.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/storage/sinval.h,v
retrieving revision 1.35
diff -c -w -b -B -c -r1.35 sinval.h
*** sinval.h 2 Jun 2004 21:29:29 -0000 1.35
--- sinval.h 22 Jun 2004 04:52:54 -0000
***************
*** 20,32 ****
/*
! * We currently support two types of shared-invalidation messages: one that
* invalidates an entry in a catcache, and one that invalidates a relcache
* entry. More types could be added if needed. The message type is
* identified by the first "int16" field of the message struct. Zero or
* positive means a catcache inval message (and also serves as the catcache
! * ID field). -1 means a relcache inval message. Other negative values
! * are available to identify other inval message types.
*
* Relcache invalidation messages usually also cause invalidation of entries
* in the smgr's relation cache. This means they must carry both logical
--- 20,33 ----
/*
! * We currently support three types of shared-invalidation messages: one that
* invalidates an entry in a catcache, and one that invalidates a relcache
* entry. More types could be added if needed. The message type is
* identified by the first "int16" field of the message struct. Zero or
* positive means a catcache inval message (and also serves as the catcache
! * ID field). -1 means a relcache inval message. -2 means a subtransaction
! * boundary message. Other negative values are available to identify other
! * inval message types.
*
* Relcache invalidation messages usually also cause invalidation of entries
* in the smgr's relation cache. This means they must carry both logical
***************
*** 53,58 ****
--- 54,64 ----
* and so that negative cache entries can be recognized with good accuracy.
* (Of course this assumes that all the backends are using identical hashing
* code, but that should be OK.)
+ *
+ * A subtransaction boundary is not really a cache invalidation message;
+ * rather it's an implementation artifact for nested transactions. The
+ * cleanup code for subtransaction abort looks for this message as a boundary
+ * to know when to stop processing messages.
*/
typedef struct
***************
*** 79,89 ****
--- 85,104 ----
*/
} SharedInvalRelcacheMsg;
+ #define SUBXACTBOUNDARYMSG_ID (-2)
+
+ typedef struct
+ {
+ int16 id; /* type field --- must be first */
+ TransactionId xid; /* transaction id */
+ } SubxactBoundaryMsg;
+
typedef union
{
int16 id; /* type field --- must be first */
SharedInvalCatcacheMsg cc;
SharedInvalRelcacheMsg rc;
+ SubxactBoundaryMsg sb;
} SharedInvalidationMessage;
Index: src/backend/utils/cache/inval.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/cache/inval.c,v
retrieving revision 1.62
diff -c -w -b -B -c -r1.62 inval.c
*** inval.c 18 Jun 2004 06:13:52 -0000 1.62
--- inval.c 23 Jun 2004 00:04:35 -0000
***************
*** 66,73 ****
* manipulating the init file is in relcache.c, but we keep track of the
* need for it here.
*
! * All the request lists are kept in TopTransactionContext memory, since
* they need not live beyond the end of the current transaction.
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
--- 66,75 ----
* manipulating the init file is in relcache.c, but we keep track of the
* need for it here.
*
! * All the request lists are kept in CommitContext memory, since
* they need not live beyond the end of the current transaction.
+ * Also, this makes it easy to free messages created in an aborting
+ * subtransaction.
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
***************
*** 80,85 ****
--- 82,88 ----
*/
#include "postgres.h"
+ #include "access/xact.h"
#include "catalog/catalog.h"
#include "miscadmin.h"
#include "storage/sinval.h"
***************
*** 119,127 ****
* other backends or rolled back from local cache when we commit
* or abort the transaction.
*
! * The relcache-file-invalidated flag can just be a simple boolean,
! * since we only act on it at transaction commit; we don't care which
! * command of the transaction set it.
*----------------
*/
--- 122,140 ----
* other backends or rolled back from local cache when we commit
* or abort the transaction.
*
! * Note that the second list will carry a subtransaction boundary message
! * for each running subtransaction. It will be used to determine which
! * messages should be dropped from the list if the subtransaction aborts.
! *
! * The relcache-file-invalidated flag is a TransactionId which shows
! * what level of the transaction tree is requesting a invalidation.
! * To register an invalidation, the transaction saves its own TransactionId
! * in RelcacheInitFileInval, unless the value was already set to
! * a valid TransactionId. If it aborts and the value is its TransactionId,
! * it resets the value to InvalidTransactionId. If it commits, it changes
! * the value to its parent's TransactionId. This way the value is propagated
! * up to the topmost transaction, which will delete the file if a valid
! * TransactionId is detected.
*----------------
*/
***************
*** 131,137 ****
/* head of previous-commands event list */
static InvalidationListHeader PriorCmdInvalidMsgs;
! static bool RelcacheInitFileInval; /* init file must be invalidated? */
/*
* Dynamically-registered callback functions. Current implementation
--- 144,151 ----
/* head of previous-commands event list */
static InvalidationListHeader PriorCmdInvalidMsgs;
! /* init file must be invalidated? */
! static TransactionId RelcacheInitFileInval = InvalidTransactionId;
/*
* Dynamically-registered callback functions. Current implementation
***************
*** 176,182 ****
/* First time through; create initial chunk */
#define FIRSTCHUNKSIZE 16
chunk = (InvalidationChunk *)
! MemoryContextAlloc(TopTransactionContext,
sizeof(InvalidationChunk) +
(FIRSTCHUNKSIZE - 1) *sizeof(SharedInvalidationMessage));
chunk->nitems = 0;
--- 190,196 ----
/* First time through; create initial chunk */
#define FIRSTCHUNKSIZE 16
chunk = (InvalidationChunk *)
! MemoryContextAlloc(CommitContext,
sizeof(InvalidationChunk) +
(FIRSTCHUNKSIZE - 1) * sizeof(SharedInvalidationMessage));
chunk->nitems = 0;
***************
*** 190,196 ****
int chunksize = 2 * chunk->maxitems;
chunk = (InvalidationChunk *)
! MemoryContextAlloc(TopTransactionContext,
sizeof(InvalidationChunk) +
(chunksize - 1) *sizeof(SharedInvalidationMessage));
chunk->nitems = 0;
--- 204,210 ----
int chunksize = 2 * chunk->maxitems;
chunk = (InvalidationChunk *)
! MemoryContextAlloc(CommitContext,
sizeof(InvalidationChunk) +
(chunksize - 1) * sizeof(SharedInvalidationMessage));
chunk->nitems = 0;
***************
*** 208,214 ****
*
* NOTE: when we are about to commit or abort a transaction, it's
* not really necessary to pfree the lists explicitly, since they will
! * go away anyway when TopTransactionContext is destroyed.
*/
static void
FreeInvalidationMessageList(InvalidationChunk **listHdr)
--- 222,228 ----
*
* NOTE: when we are about to commit or abort a transaction, it's
* not really necessary to pfree the lists explicitly, since they will
! * go away anyway when CommitContext is destroyed.
*/
static void
FreeInvalidationMessageList(InvalidationChunk **listHdr)
***************
*** 405,412 ****
* If the relation being invalidated is one of those cached in the
* relcache init file, mark that we need to zap that file at commit.
*/
! if (RelationIdIsInInitFile(relId))
! RelcacheInitFileInval = true;
}
/*
--- 420,427 ----
* If the relation being invalidated is one of those cached in the
* relcache init file, mark that we need to zap that file at commit.
*/
! if (RelationIdIsInInitFile(relId) && RelcacheInitFileInval == InvalidTransactionId)
! RelcacheInitFileInval = GetCurrentTransactionId();
}
/*
***************
*** 467,472 ****
--- 482,489 ----
smgrclosenode(msg->rc.physId);
}
}
+ else if (msg->id == SUBXACTBOUNDARYMSG_ID)
+ ; /* do nothing */
else
elog(FATAL, "unrecognized SI message id: %d", msg->id);
}
***************
*** 619,624 ****
--- 636,662 ----
}
/*
+ * AtSubXactStart_Inval
+ * Insert subtransaction boundary messages to the lists.
+ *
+ * Note that we cheat and append the message to the prior cmd list. We can do
+ * this because we know it will be in the same relative position (last
+ * of prior cmds, first of current cmd), and it allows us to save searching the
+ * current cmd's list for the message in case of subtransaction abort.
+ */
+ void
+ AtSubStart_Inval(void)
+ {
+ SharedInvalidationMessage msg;
+
+ msg.sb.id = SUBXACTBOUNDARYMSG_ID;
+ msg.sb.xid = GetCurrentTransactionId();
+
+ AddInvalidationMessage(&(PriorCmdInvalidMsgs.cclist), &msg);
+ AddInvalidationMessage(&(PriorCmdInvalidMsgs.rclist), &msg);
+ }
+
+ /*
* AtEOXactInvalidationMessages
* Process queued-up invalidation messages at end of transaction.
*
***************
*** 636,642 ****
* the caches yet.
*
* In any case, reset the various lists to empty. We need not physically
! * free memory here, since TopTransactionContext is about to be emptied
* anyway.
*
* Note:
--- 674,680 ----
* the caches yet.
*
* In any case, reset the various lists to empty. We need not physically
! * free memory here, since CommitContext is about to be emptied
* anyway.
*
* Note:
***************
*** 652,658 ****
* and after we send the SI messages. However, we need not do
* anything unless we committed.
*/
! if (RelcacheInitFileInval)
RelationCacheInitFileInvalidate(true);
AppendInvalidationMessages(&PriorCmdInvalidMsgs,
--- 690,696 ----
* and after we send the SI messages. However, we need not do
* anything unless we committed.
*/
! if (RelcacheInitFileInval != InvalidTransactionId)
RelationCacheInitFileInvalidate(true);
AppendInvalidationMessages(&PriorCmdInvalidMsgs,
***************
*** 661,667 ****
ProcessInvalidationMessages(&PriorCmdInvalidMsgs,
SendSharedInvalidMessage);
! if (RelcacheInitFileInval)
RelationCacheInitFileInvalidate(false);
}
else
--- 699,705 ----
ProcessInvalidationMessages(&PriorCmdInvalidMsgs,
SendSharedInvalidMessage);
! if (RelcacheInitFileInval != InvalidTransactionId)
RelationCacheInitFileInvalidate(false);
}
else
***************
*** 670,682 ****
LocalExecuteInvalidationMessage);
}
! RelcacheInitFileInval = false;
DiscardInvalidationMessages(&PriorCmdInvalidMsgs, false);
DiscardInvalidationMessages(&CurrentCmdInvalidMsgs, false);
}
/*
* CommandEndInvalidationMessages
* Process queued-up invalidation messages at end of one command
* in a transaction.
--- 708,849 ----
LocalExecuteInvalidationMessage);
}
! RelcacheInitFileInval = InvalidTransactionId;
DiscardInvalidationMessages(&PriorCmdInvalidMsgs, false);
DiscardInvalidationMessages(&CurrentCmdInvalidMsgs, false);
}
/*
+ * Propagate the invalidate-init-file flag.
+ */
+ void
+ AtSubCommit_Inval(void)
+ {
+ if (RelcacheInitFileInval == GetCurrentTransactionId())
+ RelcacheInitFileInval = GetParentTransactionId();
+ }
+
+ /*
+ * AtSubAbort_Inval
+ * Process the invalidation list during subtransaction abort.
+ *
+ * During subtransaction abort, we have to discard the messages from
+ * the current command, and locally process the messages from prior
+ * commands that were inserted after the subtransaction boundary message.
+ */
+ void
+ AtSubAbort_Inval(void)
+ {
+ InvalidationChunk *chunk,
+ *savechunk;
+ TransactionId myXid = GetCurrentTransactionId();
+
+ /*
+ * Drop the invalidate-init-file flag, if I set it up.
+ */
+ if (RelcacheInitFileInval == myXid)
+ RelcacheInitFileInval = InvalidTransactionId;
+
+ /*
+ * We can discard the current command's messages because they haven't
+ * been executed. We don't need to free the messages because the
+ * CommitContext is going away soon.
+ *
+ * Note that we cheated in AtSubStart_Inval by inserting the boundary
+ * message in the PriorCmd list. If we hadn't done that, the message
+ * could still be in CurrentCmdInvalidMsgs!!!
+ */
+ DiscardInvalidationMessages(&CurrentCmdInvalidMsgs, false);
+
+ /*
+ * Sadly we have to break the chunk abstraction here.
+ * We need to walk it backwards, sort of -- starting from the
+ * last item on the first chunk, to the first item of same, until
+ * the subtransaction boundary message is found. If it isn't,
+ * jump to the next chunk and repeat.
+ *
+ * While walking, process the messages. When the boundary message
+ * is found, use its position as "last message" in the chunk, and
+ * save the chunk as the head of the message list. There's no need
+ * to free the dropped chunks, because they'll go away with the
+ * CommitContext.
+ *
+ * Start with the catcache message list.
+ */
+ chunk = PriorCmdInvalidMsgs.cclist;
+ savechunk = NULL;
+ while (chunk != NULL)
+ {
+ int i;
+
+ for (i = chunk->nitems - 1; i >= 0; i--)
+ {
+ if (chunk->msgs[i].id == SUBXACTBOUNDARYMSG_ID &&
+ ((SubxactBoundaryMsg *) &(chunk->msgs[i]))->xid == myXid)
+ {
+ /*
+ * Drop the already processed messages. If this is the first
+ * message in the chunk, use the next chunk as the head of
+ * the list. Else, use this chunk, but be sure to adjust the
+ * number of items.
+ */
+ if (i == 0)
+ savechunk = chunk->next;
+ else
+ {
+ chunk->nitems = i;
+ savechunk = chunk;
+ }
+ goto donecatcache;
+ }
+ LocalExecuteInvalidationMessage(&(chunk->msgs[i]));
+ }
+ chunk = chunk->next;
+ /*
+ * The next chunk can't be null because this means we
+ * failed to find the boundary message.
+ */
+ Assert(chunk != NULL);
+ }
+ donecatcache:
+ PriorCmdInvalidMsgs.cclist = savechunk;
+
+ /* do the same for the Relcache message list */
+ chunk = PriorCmdInvalidMsgs.rclist;
+ while (chunk != NULL)
+ {
+ int i;
+
+ for (i = chunk->nitems - 1; i >= 0; i--)
+ {
+ if (chunk->msgs[i].id == SUBXACTBOUNDARYMSG_ID &&
+ ((SubxactBoundaryMsg *) &(chunk->msgs[i]))->xid == myXid)
+ {
+ /* Same as above. This code could be made a macro. */
+ if (i == 0)
+ savechunk = chunk->next;
+ else
+ {
+ chunk->nitems = i;
+ savechunk = chunk;
+ }
+ goto donerelcache;
+ }
+ LocalExecuteInvalidationMessage(&(chunk->msgs[i]));
+ }
+ chunk = chunk->next;
+ /*
+ * The next chunk can't be null because this means we
+ * failed to find the boundary message.
+ */
+ Assert(chunk != NULL);
+ }
+ donerelcache:
+ PriorCmdInvalidMsgs.rclist = savechunk;
+ }
+
+ /*
* CommandEndInvalidationMessages
* Process queued-up invalidation messages at end of one command
* in a transaction.
Alvaro Herrera wrote:
On Sun, Jun 20, 2004 at 04:37:16PM -0400, Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
Here I present the nested transactions patch and the phantom Xids patch
that goes with it.I looked at the phantom XIDs stuff a bit. I still have little confidence
that the concept is correct :-( but here are some comments on the code
level.Ok. I for one think this got much more complex than I had originally
thought it would be. I agree the changes to Set/Get Xmin/Xmax are way
beyond what one would want, but the alternative would be to spread the
complexity into their callers and I think that would be much worse.I don't have a lot of confidence in this either. The patch will be
available in archives if anybody wants to implement this in a cleaner
and safer way; I'll continue working on the rest of the things you
pointed out in the subtransactions patch.
I am sorry to have given Alvaro another idea that didn't work. However,
thinking of options, I wonder if instead of phantom xids, we should do
phantom cids. Because only the local backend looks at the command
counter (cid). I think it might be alot cleaner. The phantom cid would
have a tuple bit set indicating that instead of being a cid, it is an
index into an array of cmin/cmax pairs.
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
If we add nested transactions for 7.5, are we going to need savepoints
too in the same release? If we don't, are we going to get a lot of
complaints?
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
On Wed, Jun 23, 2004 at 08:58:15AM -0400, Bruce Momjian wrote:
If we add nested transactions for 7.5, are we going to need savepoints
too in the same release? If we don't, are we going to get a lot of
complaints?
It'd be good to have savepoints right now. I'm not sure it'd be good to
expose the nested transactions implementation if we are going to offer
savepoints later, because it means we will have to keep nested
transactions forever.
Maybe it is a good idea to hide the implementation details and offer
only the standard SAVEPOINT feature. I'm not sure how hard it is to
change the syntax; I don't think it'd be a big deal.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"La conclusi�n que podemos sacar de esos estudios es que
no podemos sacar ninguna conclusi�n de ellos" (Tanenbaum)
On Wed, Jun 23, 2004 at 08:57:11AM -0400, Bruce Momjian wrote:
I am sorry to have given Alvaro another idea that didn't work.
It allowed me to learn a lot of cool tricks, so it wasn't wasted work.
The only think I'm sorry about is that I should have used the time for
something more useful, like tackling the remaining problems in the
nested xacts implementation proper.
However, thinking of options, I wonder if instead of phantom xids, we
should do phantom cids. Because only the local backend looks at the
command counter (cid). I think it might be alot cleaner. The phantom
cid would have a tuple bit set indicating that instead of being a cid,
it is an index into an array of cmin/cmax pairs.
Yeah, maybe this can work. I'm not going to try however, at least not
now. If somebody else wants to try, be my guest.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"El conflicto es el camino real hacia la uni�n"
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
It'd be good to have savepoints right now. I'm not sure it'd be good to
expose the nested transactions implementation if we are going to offer
savepoints later, because it means we will have to keep nested
transactions forever.
Nested transactions are *good*. We should not hide them.
I don't object to offering spec-compatible savepoints, but the plain
fact of the matter is that that's a dumbed-down API. We should not
feel constrained to offer only that.
regards, tom lane
On Wed, 2004-06-23 at 13:57, Bruce Momjian wrote:
I am sorry to have given Alvaro another idea that didn't work.
No way! Keep having the ideas, please.
I've done some more digging in dead time on all of this and I think
we're on the right course in general by implementing all of this.
...well done to Alvaro for being able to make the ideas reality.
Best regards, Simon Riggs
On Sun, Jun 20, 2004 at 08:49:22PM -0400, Tom Lane wrote:
Regarding GUC, a WIP report:
Given patches for inval.c and guc.c, I would say that the patch is
functionally close enough to done that we could commit to including
it in 7.5 --- the other stuff could be wrapped up post-feature-freeze.
I figured I could save the values whenever they are going to change, and
restore them if the subtransaction aborts. This seems to work fine
(lightly tested).
I still have to figure out how to handle allocation for string vars, but
I thought I'd post the patch for others to see. Please let me know if
it's too ugly. (This patch misses the pieces in xact.c and xact.h but
I'm sure the concept is clear.)
I'll post a full patch once the missing deferred trigger stuff works.
With the patches I posted to inval.c I think this fulfills the
requirements, barring the performance issues raised.
Comments?
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"No single strategy is always right (Unless the boss says so)"
(Larry Wall)
Attachments:
guc.patchtext/plain; charset=us-asciiDownload
Index: guc.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.211
diff -c -w -b -B -c -r1.211 guc.c
*** guc.c 11 Jun 2004 03:54:54 -0000 1.211
--- guc.c 24 Jun 2004 23:41:42 -0000
***************
*** 25,30 ****
--- 25,31 ----
#include "utils/guc.h"
#include "utils/guc_tables.h"
+ #include "access/xact.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/async.h"
***************
*** 54,59 ****
--- 55,61 ----
#include "tcop/tcopprot.h"
#include "utils/array.h"
#include "utils/builtins.h"
+ #include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "pgstat.h"
***************
*** 76,81 ****
--- 78,85 ----
static const char *assign_log_destination(const char *value,
bool doit, GucSource source);
+ static void SaveGucVariable(struct config_generic *conf);
+
#ifdef HAVE_SYSLOG
extern char *Syslog_facility;
extern char *Syslog_ident;
***************
*** 105,110 ****
--- 109,115 ----
GucSource source);
static bool assign_stage_log_stats(bool newval, bool doit, GucSource source);
static bool assign_log_stats(bool newval, bool doit, GucSource source);
+ static bool assign_transaction_read_only(bool newval, bool doit, GucSource source);
/*
***************
*** 172,177 ****
--- 177,183 ----
static int max_index_keys;
static int max_identifier_length;
static int block_size;
+ static int nesting_level;
static bool integer_datetimes;
/* Macros for freeing malloc'd pointers only if appropriate to do so */
***************
*** 801,807 ****
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&XactReadOnly,
! false, NULL, NULL
},
{
{"add_missing_from", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
--- 807,813 ----
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&XactReadOnly,
! false, assign_transaction_read_only, NULL
},
{
{"add_missing_from", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
***************
*** 1311,1316 ****
--- 1317,1333 ----
BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
},
+ {
+ /* XXX probably it's a bad idea for this to be GUC_REPORT. */
+ {"nesting_level", PGC_INTERNAL, UNGROUPED,
+ gettext_noop("Shows the current transaction nesting level"),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_REPORT
+ },
+ &nesting_level,
+ 0, 0, INT_MAX, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
***************
*** 2001,2014 ****
return find_option(map_old_guc_names[i+1]);
}
! /* Check if the name is qualified, and if so, check if the qualifier
* maps to a custom variable class.
*/
dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
if(dot != NULL && is_custom_class(name, dot - name))
! /*
! * Add a placeholder variable for this name
! */
return (struct config_generic*)add_placeholder_variable(name);
/* Unknown name */
--- 2018,2030 ----
return find_option(map_old_guc_names[i+1]);
}
! /*
! * Check if the name is qualified, and if so, check if the qualifier
* maps to a custom variable class.
*/
dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
if(dot != NULL && is_custom_class(name, dot - name))
! /* Add a placeholder variable for this name */
return (struct config_generic*)add_placeholder_variable(name);
/* Unknown name */
***************
*** 2899,2904 ****
--- 2915,2922 ----
return false;
}
+ SaveGucVariable(record);
+
if (changeVal || makeDefault)
{
if (changeVal)
***************
*** 3004,3009 ****
--- 3022,3029 ----
return false;
}
+ SaveGucVariable(record);
+
if (changeVal || makeDefault)
{
if (changeVal)
***************
*** 3099,3104 ****
--- 3119,3126 ----
return false;
}
+ SaveGucVariable(record);
+
if (changeVal || makeDefault)
{
if (changeVal)
***************
*** 3257,3262 ****
--- 3279,3286 ----
}
}
+ SaveGucVariable(record);
+
guc_string_workspace = NULL;
if (changeVal || makeDefault)
***************
*** 4744,4749 ****
--- 4768,4909 ----
return newarray;
}
+ typedef struct SaveGucBoolVar
+ {
+ bool variable;
+ bool session_val;
+ bool tentative_val;
+ } SaveGucBoolVar;
+
+ typedef struct SaveGucIntVar
+ {
+ int variable;
+ int session_val;
+ int tentative_val;
+ } SaveGucIntVar;
+
+ typedef struct SaveGucRealVar
+ {
+ double variable;
+ double session_val;
+ double tentative_val;
+ } SaveGucRealVar;
+
+ typedef struct SaveGucStringVar
+ {
+ char *variable;
+ char *session_val;
+ char *tentative_val;
+ } SaveGucStringVar;
+
+ typedef struct SaveGucVar
+ {
+ struct config_generic *record;
+ union {
+ SaveGucBoolVar boolvar;
+ SaveGucIntVar intvar;
+ SaveGucRealVar realvar;
+ SaveGucStringVar stringvar;
+ };
+ } SaveGucVar;
+
+ void
+ SaveGucVariable(struct config_generic *record)
+ {
+ MemoryContext old_cxt;
+ SaveGucVar *save;
+
+ /*
+ * don't bother if we are not inside a subtransaction.
+ */
+ if (!IsSubTransaction())
+ return;
+
+ /*
+ * no point in saving this.
+ */
+ if (record->name == "nesting_level")
+ return;
+
+ old_cxt = MemoryContextSwitchTo(CommitContext);
+
+ save = (SaveGucVar *) palloc(sizeof(SaveGucVar));
+ save->record = record;
+
+ switch (record->vartype)
+ {
+ case PGC_BOOL:
+ save->boolvar.variable = *((struct config_bool *)record)->variable;
+ save->boolvar.session_val = ((struct config_bool *)record)->session_val;
+ save->boolvar.tentative_val = ((struct config_bool *)record)->tentative_val;
+ break;
+
+ case PGC_INT:
+ save->intvar.variable = *((struct config_int *)record)->variable;
+ save->intvar.session_val = ((struct config_int *)record)->session_val;
+ save->intvar.tentative_val = ((struct config_int *)record)->tentative_val;
+ break;
+
+ case PGC_REAL:
+ save->realvar.variable = *((struct config_real *)record)->variable;
+ save->realvar.session_val = ((struct config_real *)record)->session_val;
+ save->realvar.tentative_val = ((struct config_real *)record)->tentative_val;
+ break;
+
+ #ifdef NOT_USED
+ case PGC_STRING:
+ save->stringvar.variable = pstrdup(*((struct config_string *)record)->variable);
+ save->stringvar.session_val = ((struct config_string *)record)->session_val;
+ save->stringvar.tentative_val = ((struct config_string *)record)->tentative_val;
+ break;
+ #endif
+
+ default:
+ elog(DEBUG2, "ignoring unsupported GUC vartype %d", record->vartype);
+
+ }
+
+ TransactionSaveGucVar(save);
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ void
+ RestoreGucVariable(void *pointer)
+ {
+ SaveGucVar *save = (SaveGucVar *)pointer;
+
+ switch (save->record->vartype)
+ {
+ case PGC_BOOL:
+ *((struct config_bool *)save->record)->variable = save->boolvar.variable;
+ ((struct config_bool *)save->record)->session_val = save->boolvar.session_val;
+ ((struct config_bool *)save->record)->tentative_val = save->boolvar.tentative_val;
+ break;
+
+ case PGC_INT:
+ *((struct config_int *)save->record)->variable = save->intvar.variable;
+ ((struct config_int *)save->record)->session_val = save->intvar.session_val;
+ ((struct config_int *)save->record)->tentative_val = save->intvar.tentative_val;
+ break;
+
+ case PGC_REAL:
+ *((struct config_real *)save->record)->variable = save->realvar.variable;
+ ((struct config_real *)save->record)->session_val = save->realvar.session_val;
+ ((struct config_real *)save->record)->tentative_val = save->realvar.tentative_val;
+ break;
+
+ #ifdef NOT_USED
+ case PGC_STRING:
+ *((struct config_string *)save->record)->variable = save->stringvar.variable;
+ ((struct config_string *)save->record)->session_val = save->stringvar.session_val;
+ ((struct config_string *)save->record)->tentative_val = save->stringvar.tentative_val;
+ break;
+ #endif
+
+ default:
+ elog(DEBUG2, "ignoring saved value type %d", save->record->vartype);
+ }
+ }
/*
* assign_hook subroutines
***************
*** 5133,5137 ****
--- 5293,5306 ----
return true;
}
+ static bool
+ assign_transaction_read_only(bool newval, bool doit, GucSource source)
+ {
+ if (doit && source >= PGC_S_INTERACTIVE && IsSubTransaction())
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot set transaction read only mode inside a subtransaction")));
+ return true;
+ }
#include "guc-file.c"
I said
I'll post a full patch once the missing deferred trigger stuff works.
With the patches I posted to inval.c I think this fulfills the
requirements, barring the performance issues raised.
Ok, here is a full patch that includes:
- all what was in the previous patch
- GUC vars are rolled back on subxact abort
- the inval msg lists are cleaned up of items created in the aborting
subxact
- SET CONSTRAINTS is rolled back too
Missing:
- all the other items Tom mentioned in his last mail, including
performance enhancements.
- discussion whether we want a different syntax for subxacts, like
SUBBEGIN/SUBCOMMIT/SUBABORT instead of BEGIN/COMMIT/ABORT. Please
comment on this point.
- support for SAVEPOINT syntax.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"La grandeza es una experiencia transitoria. Nunca es consistente.
Depende en gran parte de la imaginaci�n humana creadora de mitos"
(Irulan)
Attachments:
nested-all-15.patch.gzapplication/octet-streamDownload
�r��@nested-all-15.patch �<is�����_1�&)��~v�,+~�����c�lm�@`(��������g��SR���Es��t�����h��N��M~������(�p���]a�Y�F��v9�C���w�v���^��G���������\�8�������Xz��j=o�����t�����VK�c�V�����l.�������Y{��sp��3�>��?���o�t=2�6j~�|�Y�����s����bqmv�c�no��-g�.KBvc������c�|� m��wk,��p��X�D���7g��>`|�����jg�����5�v���vM��6pT�����d�_����`��7�$���G�/:X8b@�g������qk�);�v�������
k�c�F�
�� ���7���_�\�]^�{o�
��� �j��<����W��v/�\�q"T�?z���\09�0��7��-K����iX��l��>��+F���������X��5��]
6�}�e=�~���^��F���7_�cb�
�e$8�WG���_I��p�p��F�M���V�������Q��o �|�&�g����5�����F��(����#�p?����z�=������Yr�(J'�N�d�C��,6����~��[���H��
�K���������k�M�H~2�8��
�M��
��*e����������$�����;p/���%��UX? �c/��=�f�:M�5��)�sS��VM�����$�"$���7�L��'�����E\o�w��J����s���}�m8�yd6����m���q����?�b�J�����&d�'i,��P$�>����>O� ��� ��\5)�9k��'i$d��U?����J���N_~*u������S:��6�b��1lp�;�����1U��������:d��}/�� P��"����l�1�EIT��Hk�d��&V�q�&H���c��b���_��!����)-v9�<�m�h����v���*�0�. +�$�6��C�v�f���L�)��\k7_zxD�B���;��_0�AHfM�!�>�"�M�}���*B��k[��������gd��
m���;N���xF� �J���5��� ���[,I���p������t�a�E���*t^���J���W�J�Z���CW�[L����z�I�h{+�X�����3a[��K(F���:r�BG����DrE59E V���E��q;�!6%Ei}�t����"��\Q���M����|�l1�]@�X%6����\Q�J�L�2�3����63���z����'[��kD,-b�}�����y�u���\����������9����G��T�]>�������������������r��O���9!,����d�!�����B�^��������{��B>$qU�a_�*�)2-{A}���s
L
���c�g���d�������M��XP<��X���<0��J4 �g G�6��iS2
i6Z�e6Z5�����a�N:7J�S�Yb����Y�cN"���� �*>V����E�7]�E��A~�w�fxWO��J��<p��5Es��Z�Y.�0��`Jn
7�t�a*~;�YzO<��������6��bZ�T��B���e<8*j�3S%�[-��~�����X*N�q.���m
�fl�\��(d�N�����)�������8-�7U��O2#U���������
�-�O
!�Ynx4�qM�����g�����h�G�fL�~#-��.����������VI�,�N��K��,�j��qn���g��o������{V�y���-���
���7f�zp>�P�r�qj_Hh���m�b�X�:#���I���r��? 1i��/�>����HS��J!�K���������`�P����\TdT�n$U����� V�[d����-��lk$%�!���� B9(�>����gbL�4 �UT�4�b������BO�@�^z�m��c�SGoe�!���+��)8�d�x`�&J������s��t�9���5�`W(�9�v��y�1��b`O��N�P�B���NoVQLqno��"�Y���?�T�D�ka�
Z-LoN%PwZ2 �J����J��V�[�
���"���Q-_���:{���hn�A.5���7���7��%}��%u4Z�Ko���h��}}�aj�K�
�����^�]��m����m�X���P�I��V����C��_t>�c���� v��_��)}�U����^��(��T�"�t�T��{���h���ZE(�@�K��Qc��r����z������?��e����+v����y��-2��9�.Q��������ro]%.��.��5��L]e�{�i�.���/X����d (D�;EW �b��J'��HwF���}��s�-������C��E2����*�i��-��w%#���YA�����(
�^#��)�c���on�������U�/�1�bJ�&��1�eXX]���+�M����~`~F��x�od���9|N~�,);�n���������R"e��L%���3;��M9C�����'|�DJ����GI�4F�_��/��"��__��N.��=��:}��|~����N8~u���
����s8;��o��� l�$��x��g�����'���g��f%� �}o<��y���s��������w��*�R.$P��|��g*�y{���
�H�
y�C$����5W}�QT������l�x#�������7��@�������������\L�k������_@MJ���
�������K$�@����� ��-�������Y��_G���^q���gOd{2N� S�)���$��ew/���-:���U���P.-�z���=��n�"����W��y��c�eF�����y�Zf���to�t`$[&�B��s�|� �z��e���������, ~����g�-A�_O{��W������ ye�g2�sg�����4r8����(L��������8
"��uD���w�?@>���}g.Q
[�o$����|y�����������O�=�'��0���� �D�#�E@������+d��K���D��9�;Q+(9S`[��G!n���Bbd�1���L�|�����D@�D��H��3��S)������_4��"���>�QHlt)H��@\;�|���HN������d��)H���,@��(�e�;�5�w��/J�8�����q�~�~p��I���g�a�c'u������/.?\�k���������?S�����\���b��Q��-����)�\�hcNy�c��)|�!�7���y�x�����<�/�� �Dj8�/�f��l���������Jo�� �v��c.�F'�d0L=�u=t������T�����a
�N��u��C�\�|��}L�`����~}�#8���v������h|�W�G�V(���8T��E����J���}�����G�aZu��^_P~��G���J�������}�I'����� ��U�,����������I������`�)�)���>�)];��0u�#i�����9t�+c��GG�I�T]�`u��g�����N9��'z|�'�n�������e� �R ���?��ta2",'�O1�~VLF�n�D���x;9��,�{,�g�����������e'��� ^�#O����� :?8Z���r&��{������������=!m�]����:������)����S����kS
��a�������2���O?���U�l�^����-:��4U9r�����2H�����
�G�YUW�]P{�}Ur�����C�f|:����R����H��[D-�h{>��A3sM�d�7E��������9n.�k�d� ��b�H����H��!V�K3�>��ZEH�E�4`q��Q|j��)Y��Gv�'r6��;�8;�@0�Y|
������ 4?���rt$!"�%�u�-�|���xv����XN\���XV%�d�6W�IrRv���8�AR ����|��E�������E�g6��>�=��,h,h�?(��@gV��������hK��V�@�a T����G�W�j�x�\���S�����&�����WX�r=P�������vM���Y v{���DS�c��E�����\��T��0v��#P;6�t�X�Q�8@���>3f��� �1C��B���kH���n�2�W�g�*/�������� ����*%����>���[���y�.X�#&Zl�&T;��� x�0��/�t�`�?�p�Ca�G��l
�<�����+��I8���s����`����V�k1M0�PR������L���
���<P�-���X�'����T;��� 4�=�l
��������,l���@ -�{|#'H|t��F�p����(���=�w �����4�� g$*�T���[����;�;����w�������O�t0�h����5 ����uyq~_������_0<.1Kc�L�������Y�����^� X*U�iK8�0��aC<���mdO��T,�l��$�Y�S5���+�l?ir\9{;3��j�?xw�������A���mz��?}���Bk������c�V
B��&��x�<p��T�:#���� ���v�$%�� ��c��P"��k���U�`�yF�z�K=���w��88q���0��d�B�Q)�� � ��sI��b����<����<-��y�k,�du���
�������B�D �*W�a���`�����r�9���^�[Zi��� ���H�9�4�kT����@
C�,�GQ@�:�E������e�Y�/�@[�..�� ��v g�<����5�������R(y�rjQ ��/T��GYP�i����
G\��[c�0�h�G�4���0� �c��D��(G���:�a��A7���v]��G�l`*���1(��b��t�Nm�]B��O��7��r}��U{��r���Q�9��sW:H!CG��T�:�p|�"d�:�R�
��:"���<�N�p�7_ ������l`u7���B� q)��Z��m�R�e3����}�C�����
;�_P@��;�i7�g`T�4�.�}� ��wQ�+y�
v_�5I��<}��'�O�
����G�Q�$�UL0�c ����X���_j����������"�T)�l~["���>�.��{j\�"l����G%+-�o�R��]tW=y�T����3
�>O��B����,^�W��4=��hD�\yl�~il�Q�f����Eb���?
Tu��fe�� ��?�Q�e���!���^���[X��5k&�*��K�"��������C*�t��B�i��YC$��E��-/��r��P[����t��&rX�/#��
TU���P)^%���m�x-!8\&�s���9g� ��P@[WstzG��h��5�!�4�}�]H�����)i])�D�DE���K����g�/�����r��� I�C��������r}�-�R5
�j�#�>����?�����08 N�h����T��W�Y�Y]���"��j��^���v�~A[g
K����gG�/cNK���`E�Rt�]��M@�H>w�s��o�y����/�� ��\R-Pf>
R��w����;� ��J� ���6�[#{��z��z_��9�s%3����H���G����T�.�`�V�"�����K&:Hu�^�GYB��o`x��i)�G����|�n"����B�LX�2t�zj��:<k�Y���
YwA5�������(
6�K]C�����S�����~��w��"_+nz�`�����w�|��^�i�.������q:��B@"U0����U��=�v�"���VEL��6O�T��(z���0�'��&K���G��xG��C{ �iF��D�-��1�RnT�!JK7�r�+��8�1&�{' ��L�T��W��}�s�2
���S
J���g��g��Qx��\���Z"���N�,Gl�c��V����V���Lu`�RJ��$���Rc��N�_5���G"�d��[�o3�B9pW_[� �������p�7�����z�����L���21k�`���
B�\��n
��Ze[�&ue~���O������
� ��!xi��u�%�D�iG8�/�H8�����"���y���'hR�/�����:*�-�T� ��A ���I���Q}�:Bwj������m\}���|��4!-H�NJ��^�D;z�����n��xQ$$�& -�����w�Y�IN���ZKf��9s�,�coT����/ k�\vf�ow������'T2�����������g��~��_��=�jxoP�fK��T_/�.��`��P����!)2E�e�|�q4��w�7��t��lc�.*�AVj���T��"��Q4f�����6Y�a��������_� �U���]4"E,���`������$ s|�,,$�f`a�;� �Y8�I�J�c>N�i��>�N��z�/�YAxG�=��|2
8��oY�8`"+3�<�� 0{�
0)9��I�S�s!��N~l�T����Z%[�������v�
�*v�t5�?H�
Z_���`���9}=�-q�������hMMlh���^4A�$I �
�9C
������3��PXCC�2"("o�pI�����Q�b#gf�~���s�K�2��A=�����k��P
7�Q��j#>N�����#����D�����\���G�$���w|d���p��[�G9}��E`+���F2�'B��&�>H����&��������������5�)|�����\^8\D�ra;!}^�x>��#�����r}�~0A'/ }4i����^��D.��a�|\z���� B��[K\8D�phc�G�����U�h��6�]�
q>��+$�8hcu��X o������k�EZ|�MC���������Kw�<C0�1K� ���I]O^���<�R��� �8`�����UP�e� ����C�
����C�Lgo���=N�T�9�C��$�OX����,��&�#����6���(y�fI�w0�>Bj��� 3�&q��O����/��qQ��.Gd2c���h�s��dk����S|j�t���g�G� Iaa�I�����r6�3l��Uo��<��v��IC�����7HX����`�7���?��������
��>/���Vg �����bQp
3j?A�*:�)cPt����I�� Mu:)
�S�����������`� ����X�6�/��g�b0�O���~�=t����7w�����.�5���J%���J��"���s�/����������*w��P5���P]������J\���{-0V5M�)k2^K�;r���D� �� nA��D����t/��%�����n@7V�}�>�4�q� �X��P_���w���� M�HX����9X4��3���YJ����������8Y�)�\�
�_�o@ B��5���,���Z%�����Z%������7���Hy����w���k�u�����D#�LN���qurUcErU������y��W���k+9�W�~������f6���(Y���z�R�����9��5�FEw���R0�)�B2�\�(CM����+C�.�wY��>����
c���Xf�0����a_e?�Q|P!6 �z@:EjH�mP�!���H�V&%���9����"�{�.|�����W��Nf���Tw���o����CW�+��
*F�E u�cx�)�(���\�@i��s�����M��l \j&!;�K�a����Cw`� au|���<{��v�"bX�24�(���n�������D����a�t���d�P�:����b�4e�3xl�e�lL�/?�n4��_�����Z��4����j���g�U}���t>�����(���_NGA��Hx�m
y����L��."�`J��B�s�y4�J���#y�v��� �$i���1)��l�6C��}�YD4HZ�����d��������[[�8�\\������/�F�K]���
�*.$o�c�g�QyV4 ������5H�4�}�!�X�aa����|E���8Hwk� !q%f.���84zq���U��������l
O��g/CbN�;t��+�Ay���5�dn�;���$��|~l�/{b�
�������������S�pt���<N`�����6{5��r��������P���x2��B�����k�����Q8�w����`�i�y=����p��C��1u����u{�ecP!���P��g)�����`�����ct/^"[#[;�<��������8K���/&szf��&G��Z���'��^Z1��%1i����M{�������l�ei*�M�Z�g����,�FCo��.��"]��8�:���T�xh�B�<|����u����[x�p��a��b��o����c�RyYm��m ��#�[xe��G���A�h���������
��O�
&P ����9�'����R>w���8B�>Ii�m����H�����;���G�\��V=�VG�����\����)��D�O��y:?��(����T{�x�������H��?��3B��8��_�r�]�8��/vlI�q�2��j�/y��xrBA��v_@O >�O� ��*^��EL9���X��yIg{`�"���EF�X��`Y����h���2�{����U������83�\�"����~��,��h�q�����O>Q�uxq�U�.5�N��cW�[Ob N�[��WlN���kM-��t �b�r������4���xb��t5I
Br";���^�Rj-�hk^~�w0Y�:s�Fv�����D��t&0��s4���
(
Y�<l���w�]Z��aO�[2���<g�/��#�c�h��:8�+L5cN��I�z�$-3�:�L�b��ilda�o�q�������5�i23�Q�wr��bX1\m������!_i���������4� �+D���%� -o{l���[E*��[��;"����3���N�[��ez6[� �]��#+�h������F�w��F}���$0�T����k�U�� l������,��H��p:�{5�#��M�e85��w �S�#�'vqD9j�@����MB���y�u�,��3���������}��\�������"i����6X�9���L����������ND����;��k���^�;�7��&YN!J����[�(!��O��pch������ 7$w��fH��P�����5�"�=~p �i���)w�O�e�����~��7�XKdH�h��+yz�c:+r�Y�'���k �U�m��$����L� Eht���� f�Rz�+��p2�2��O7�
�d�D��wO��3t�}������PF���h�����������0��M1��]JY{N� eH����������+a��/�i���w�&Q�1'�$��,O�d�a&�[���x Cv���,�
���"���S�������+R���;���)���|����ZV>!����4V�i��^��Wo?��N5[m��E��W^
��Ro��F�8��A+v|��C�/��p�����VO��y�(]`.R�n���#1�@1�9�+� ��*KASfd�*e$������kDB-s�Q�,}\g*���Y6}�~����_��^�H����E���
wXQT�Ks�
�J��-�I��"����5�3�sv�ys����wtHe
�l��O������9s����}�y�����A�Lc�����}+uvz���v�6'���_��:��?Ym9��0.,4��V�a��� 7�JI�JM��|�J��yr������ti��]t�^4�l�������g�$���q�������C�J�L�.�G��R�~A11�+�����;�H����D\�S%�9�i�X� �+d�����4�Iw�BW�J?cF�UaU��y��zd���7��8q��q8��8��OO���32�-��&����)�*!n� =��n,J1#��~J�'�N\����F0�� \%���p ��Q�z~������9� �0(�5�!F������3�Z��d)�f<�a�AU*�����N#�V�m�?(�m�S��[6��%��i�@�@�8I�\ry��y���
��_�V�������3";xN���������*����\]���Z���� ���,��Y����oi�)7&��x.��
sk��%GT,5i\Y�9���� �V�6'�j����;T�]���cR��^a��B�L�(fIG����b�v��/*��5&�jy��������>S��C�{R�3�F`1~�x �@��}G�/L��^w�
��^�a>��-o�@�X[)�.[O[�}�}$%������s-Ty���n%���V����U��.������!gXv��X��7��+��0�K)�.c��$��T��>Da�[<��]^�s�.��g����Kf��1x;�����.4�
]�@�'#�����T ���+�!�jy*�O
LT�T�Q[����|��?v>D}(h���SWQ���i�X|���#�i
����� U����pi)�7xY�^/�u���8�<���F�"~"XJ���vPy���D�f��r�o7�l��� ���|��� S�;/,���t�.(a^uT���CY�#/(ZT�y�P� ?�j(=b!�$�P�m3�m����h��q�E'�M���}8$h}��"Pj@#���VD%��I�!p�S��_by�P�����u>�8n��H:Az&�p�M�
K��a0�Q��)]�i�C,��38S� 0��T���.�
�U��u�G��'3j�$�����l(P��<�E��E/i�Pw�r�����3�u0�jl��C9&(�w�-���<�nMe9����R����.�Tl�D�86���U���E�
/+$���c`v!���u�w��x����Z���m�~����,�����?��?�<;��Q��M�-������a�s%����������:��R���PI� �D�����3n�����^V&�f�oU��?�d����d���J�0�.bg�!�S?b\�����xL7��P��Q��(�;Ep��F4E���m����o����qy)Fh5��6�/�5*���T�]z���0��Vc��`��s~|����^�e
�E��B����.�P�����'D�N1Y����
�{�4v<^��Zl���-a�!qg��9Q�9f�g���hZ�m��QD)�Zuon=uo��}��
TG�?i�n-��[+n��U7���tk��i��/��[����5�����r����_o>��Z�oif���~��b�������@���R*��+�gadX���bg�
/�>K ��f2���� &��=��TX���n������\v�<�'3��'4���0����>� ��O%[��Y������D>�z�@�?��8��l��(O.���j�!����U��; �7kq���M��F����|"��W�cPTr'^~���_r���RY�4j?�����Rq~����@r��C��NN��o��@T���O*�������������j�>s�^H�7c5DD�Q��|��>�����E�b��c&� &� ��/+�=�
'��z�o��Z2�-��.ao�'a���s�;}Bs��/�9��od����[�M��k�s���hK,�p>�z�#D:�I�,�����T��U��,�*Xa���(@����s72�T�EEA>X.iKM��2J��$z���0-S+;�������32$g R��7�F�b�������]rN��O���*O�b8S�������-�]�r}��n�&����K�'�H�Mn"�K0�1��c�
dHROd+�d{*�5��
,���aOMd@'X��5��e+���<'>eE�����������e:gG�����^�C��B����f�J�oV�F��P�]a��C4�,�)����
o{o���_�V�h"�"�Y+����N����[�!@��=�\c:�Y�o�\���m1
M�����[�n����j��o�^���G�%W�2O���UM��Q���py��&� �Az�B��I�jD�A��$
���(�|_�
G����;7�bX��QI��t�a2a�:s(&�[D�%���)aSQ+�%s��WX���-�=y�a1_� ������\�[�1�,aB�#��J�"��>
����h��|��8'?*<������_��$��5����Ae��0����9�}gRs(��."������=�E�<el�������k0N!�T� "JJ���[v�����r��
8%�uC\�[���:V�
Z)�E�hO��>��A0��a�(��.����������SY���+e�����TX:�
V�yi�Zvz��jO����v�V���{�J��U�e���(�
N��jJ�|�Z4�@����wV�����yI$�Y��}��<�`�]nW����5���:�g�&���7����{����+����1�4|�t�� e���dd0�3��E��-�E9�Z�5�K\hU�~�j���������RN�%���WQ�����6�6&�h��D��I��}�{�Q��H���v�����(�{)C��p�6p*�����
*c���d��+^��&or���������u68�,ZQ�
�������s.G����U�{
����Q���Ej7��M��J�
�^��V��=��Z��T�����v� �
�P��]
�Nj��f�`��W�hM�t�-�\�P!�K+��|�2 0 X���";�
�Yg����I����UX>n~�L1���<r/�s|E���Hc�f�������Z� W�sr�ep!����\K���rk��;�v=�� ����V}mq2���S��a���r"+@����u�A��O'F���aW�������wK���)�7����OS�������,�����9�zM�^�r����� Vu�xn����b���|�|����[��;
�F���s�Xn�YA*�
�P��I�R���*�S��e�4��0��,�(Q<_wO�S��h)\�_,JoynI�+v%�eV���]YV�R��v��7b�������=��?�?*����N��aZ�e�*!0��F �t#J��S�fF��L��H)�5C`�gd�5�0��� �W��.������f����nb���?��+���Rm��JMK�_b��0f����b���r�;L.����
X�����;+ ��B@"XQ�#����PL��� )�?�n#����.����4����Q�B���������b5�^3�B�Y�Y<*�5�u��b���^��u�>���n�IshT
Q���?If�S��kpmE�(A���
o����9m�&���������r�Z���9n��iF*���Pw����H~�b��E ��d�/�*_�K�A���Y��kZ�
�A)�ZN�Y��?�)!"[4Ol��_'�,'r���n���V���x��+��|&�E�~J��Uj�
�^�mX?���3��6YS��q��v����
����-?
dm+o����Rf���N��������
��9���%��z�8����|�a<�&���'<=������� ��9�zy��b�N�;��%��[d��QL�z�M��-`��T8'�p$��
@�@�����K��~f���n�.��3k��� v���
*��bR
�lsn}�EsBm��M��|���x�\o���^��&�
��\�
�Q����p��T�����4F4�mq��~�jq�����������o�(fU�G�*%�\�Y�n�� `ub���j����u�_dT��
*�&��u�?hS&�^�`A?�(�1����4���������#|[JA���u��Q%[�����w��-�0��2>�$�9���#[xI�p5%
)h�K,�Es{�|���n����Tl{���Sq�]�9Q
���=>��dFT���)�v
���[��pF�'�1�������i�?$�^�� �;���'�3��f9�-+��`�jxbl��M�����h�N�q�.}0��,��j�R�]�qf�����"�b��RxUa�0��j ����a�A�����-b�a��y<T%%��x�6����'c��dl(O��j���M2��BJ5�T5��p��hE"�x
�n�D���?��c���R�e����g�����a�0�TL�������C�y�����z����a?�=���������,������:-��6_������������K���n��4xJ�����}�I\�e��d�����q���=/�f��@2���������;���4 1()�T$����Gn|�u���y�p-2��eT������Js�a���b/����b1�O����f)��8L�7]��]���a+K� �� ���vt����Sr����w��%o��w2�T��������w./�/�V"�7W'+�����Sr����v)�Z���Z�3��mf���06����C
n:��f.B
e��>_Z��_iUk6�?��r5����y���<�6�;d���v�M�l+q]�:���[Y���=���<�a���Q�@8I�a�:iwT��8]m����G�m�������Q gh�)�.`�������d(��F�HDCB�[:]8
�|Q���Z_:;�r��C'8R�
/JIz�5��_��� ����lY
b���Y��@�C25i D��*�;R���:�T�?6:���(k5�T�����G_��-��E����i�����O���l�KC�=��L%����@�#c��(�g��.�K}�nyy����u�G7�����j C�����
u��
U� C�bI:*�fh;�}yQo%1�1~�����N��\d�U��,���7�Z!a�K�&3��}s�w��JJlU9��I ]x&�Zu4����BGw���;�53�S�.�.
����*>��[�x�I/(���<��6_��TC��K�_���$� ����1�z�,��c�7�H�U���kVx{q�P�W�~���1����dh����D�B�LV�5�����������LV�6G!��n�l`q�����z���K��u��J2��r�r�+�0����*����]����$��@��9��w�p".�h�C�}a�S
�j�R�������o4O�N��yr�W}z�^h��sm?O9u ����K�:
l\Z����RIS�x
�����Nuq?vz�����\IU���8�-n��EHq/��F7K��%�X�Y ��(��l��0�� �/�5�~���n�I�F��m.=��)��%G�p3s���e�whI@��f�m����PK�����J>o�-G����
��>�>��t��l��?3;�4k~�Z."�c��6���=T�<���3�E6K���=1�
��@
x�n�`�� I]��2��5���(����V�+�;�3L���������#��5���+�n��=�&�A^iV�T5P��U���Zm�AV��
tH^SzV�z^7���G��"sZD;�1Nk�T�u5s��`>���^$<c��5�,`��i�LBv]�Fa��������}�H:&
�
��A���d�a�m���O@�@<
��O�M�n��"I�,����$�����6���p�����)�z����Tv,$c�gh���`zN���)���Y��\����Y�z��a� | Q�3f�!F��KVY,�T�M�=�+����,��g��4�����h�{�������0��n��]��3�Ec�"����t"��O��]��F���C70����o���>`p� )"�V�����0��+Q"4C�����z��Y��N��[�{�
3�R&�����tK����L|���|����FZ�A�=��W6!
z�>U����������z��&�Q��W��"����C����-�of{���j�����Y0���g����K9�x��6�y�$����!q��e�L1��c\�Kq��#M���7�y�����8�h�Z�O�Q[�7kFiqv���`����b-VW@5��m{��!v�W��n����e��
�Mm����
���yJ���[�a�;
g���B
��[��� ������[�)�*�x��*�����RF������%L��-W�������I����l�Q��
N�+���=w�-�������\�����P�l
�T�kp�T�������G_����; ���?�����I]]ia�]�fd����U�Z��>/�
���%��5Q���VA�.p�Pw��8U������H������u�;�����!�KJs B�`�[��kx� :���o&��Kun�>h��8��T��3���r�R�~���<���7tX��zK�:<��u��.W�R��T2��3��{��[E)�����&���p�h���R�n_O�o��t�0������l?����?���p��<�����N�����?��M�����y����x����e�a�x�j��t�&p����Z�������gv_� ���\be�r�'O���'YoE�����H��/)���(B�}��qF)D=����'�m� �[��ss���M�r� Z����z;s�vx��K
%����Y���
R�k�I�=7
���&�-/3�)�\��i:y��C��Y��`nm�`n����n)�f���;���F�z���������'e0&w
�+{��X����D6w�-3T~>$�ac���G�Hi�M 0%�-���$�9��f_#
�e|��k��y�@��xq�q0����� &W (� #��5�C����$����v���w�3��i�|�������4�8��3����2@�{��L�ry,��aL���R�
�f�`�'*7��.��L ID��������}���e��4r�W�a��]o5D�@��C=GKc�W�sA��k�w��H6aQY����,f�F����K����9���Sz�}~r;�
��wW�|OEbXg� �'������a�1P����Gy%(9>�(��4J��z���+^��Q?�|�u����.y�L 2�����p&w*��#�Tg�k�+�u�C�k' ���I������2]')�1��kB:lo>�����vV�����B��w?� ��O`c�i���"�bb�G�z��?����x�1*��e ���AG��o��;�y����Oi�sw�'��s|.8�� ~�:�x�S?�-se4�������� ��9yC���}<^4�Lv��c
��z������c���?��*����������c�8�`��+�d���KZ�N��i'����K9��\��/���[&�������6�L���(�A���2Is���L���������A���z����B*��::N�bb9�'��#L�smMn�1ty(��
��B��t�!����^y��@J0 #���l�zOb �)\o��"����-�#f .g��v� eZZ���R_�+��43��o�D�@q*u��)��R�!|L�e[���^[��lYr������c+� �_/S�+�Y���R�� A ��
D��OR���Zu������3����B��Q5���riz�g��E>���E=ZH �e�YTP;��^�E�����n�0��
,=���N�:��4� o��D���To��6E�14u����3�)�_����p�u\�:�2�4����aL)�����������*NA�S ����/��XLV
^e4�f��jd4Vh��0X���7�&����&�Y�j�k��i��E�t�b�y�����\�%������i/�����s�:�=$���<)W�2A��?��Q5($����$���NJ�+yNA��|��o����H���a�s�9Bz-����l�����s��
5��Yl�q���8��%�M�C��}�8��J?���Zd�b6F����oZI�0�D�j���4����vqU�ITd\���2A<��~���r j"���*��FT�x��_r�T�|K]3O�Uf��L�n�;x�������x�]�Jf����yOE�uW��,~�������k4c�2-��lS�
����p>-B�\�cnb����y�*�u���D����l���f��)���S����P���(�?[\o���248������3�Fa2���t>���d����n�c4d�d>�bj�RU�+�zbiF7��QH~X^�[�7��q���,� 6r|#t��C9�dj*�.���Ah�(;�@� ]����d"�F�a`������!���d!M%1}1��>�1�/��W��bb�G�>V�<+i���K/�-Q�Q���~r;���})�.��z[[��j���d����K�9�_�i�W�H���W,I�����6r�]�����M&T��l����;�����)����35_/� ]"����cM?����t�k#�Y��#3�G��&��Y*��Q�.2�)g������M�(y�vd��(��_��}��q$��JB�mM�0[r����(�9�
5j�' a�������p����5w {o���C���������f�BF1��^���'���[[������w��{R�^��/��!�9�E���o�a4�����"�MGi�cD��=�[*��(w��^Z� ��,;�fV<�ueC������?���zg��p��U��a��������� ��?�7����D&��n��Ww���������n�?��b8 ����"���0$-�S=A�[�.���^���Ss��$�5�e~��)c��@'��������/�G�wda�����a_=iJ#`6%��C��e���F��f�n���-#������Wi�/���u4O�����QJ��t�����y��v"�����)�������p��G��a�!U�.���}8Q�c�M� @���G��og��q�+��+�#��.�`x(�#�O�@���no>���pj��8�?���-B�����)�/��\0�}���E;J����e��
��������b2?���A4��c�����x����L���������[��E��_��1�j���"��A���<j���I��F��v{��M\��xg���A�C������:�T��n���+�����5_����-�������}6���O���F��W�b;��D ���Q��L��K^&���������O "��J�?��>./N�c���r��]nn�k^���h���;e���]���"��:}P�V+^��W��+�2��8^��W[&p�_U�Y����I���8�����h�jStg����Q.�J���m�j����E9-����sX�F�41�Z�&>R8."��u�0I�T�&���e�0�2],sG���S���^��������p �(�.�*������&<�t��O��zHE�f���w���L�(!�p��F���y ���'�9��n����~�f$�=��Y7A����8���C��}���Z_Z�w��DssT��=������u���l��z�j�����#
��9;�^_��������o�R?L���h�����E�t3d��|��lM�;$/���d����K�1�t9�p���W)�U[.�������>�m�������y��O2C#>�9��M������������/\g���B79�n�)�2��>Lv
SN� �Zp����:��n7��
�\G���
oh�!��D �PjB��i*M��.�@.��
_Y�~�En����\�=�^� �8�� ���x���k�a�=��p�C;lH����Q�6��p�I�������VKm�������h6&��L<!z�u'#�E�8���!��_?p����'�`_��$�YJS�@�b��Ja�+ ��I"c��MgT4��g��t��K
}<K�#�fQ��A���������>�Wx`|��Y&oGDL�$�A�k�T]�H���fa��[�������\�V�5�z]�~�k*������Y�������a�����d>%��r�xE���'V�
dE B0�$S _�Ix��>��\��#��@kV��`�2�lc���/5�1Y����dL�g�7 ����I=���:��������Lt������~&��=��o��p�ioWv1��t��,���b)x&���0���E�����pD����q���������:�Q�w���qx<Od����L|���A�8������h�O"q�|~�$nK��cq\��t��:.��\N6��T!�r����}q�<\Nl���Ht�6���7/��kgz >\�O��hEDBe�N��h�wp�a$�:�)��\�Y�n���6+F&��z�-a������Y O��/<p��0Y��c�^z�1���J��U�{��#/?�V^������vW� ��~����?5���E��V(��=�����dV��o��(��5��~
���$al,����Ksx�C�B��� 7�!�z:�**�-<��M�C[-�w.��������7*0U�l�,6������*#�5�lF�����P����S���cz����"�9�"�G5#F��)Yg����X�cQ0��~�%��n�`�����>�p4� ���JBbd��D�K��xC.O���j�sk�--���"J�7�od��-7&��0�^$l�����h�z����s�O�@�6�,(*KgJdq��C9��/��2j�%�Mr�$��d+�@v-1D�/^I �����@�Q����BC^iS�?������|O����5�F���w����������5ez��J!Z���Y�$#�S~|~��i�����,�=��Go��"�5���H���J�����K�|
�A���:���PE����x�"�t����}�����}#���]�
�8a��
��o�����0����Z�D������� m�t�i�\�Dl2{����<4%�Z�r
nA�dw�O6�F! ���2�
���[�����K��os��ta��EVee����,��:�t�R4���U�;�Z�Q�7��Bl����t3��B�oo6��`�
e����1z)P�.��:��x$���2�C��o�g�x�<�*U9���c��}O������+�0�C�'���Pb�=�|
�YPL?�������$9/���QA�*�t$�v��7����uC���a(������@��n�lm��4�
C1�h[�'�s1JO� ��������
�2%� n�;={ :y���p�D+I��(K��c�6�O��'u�|�����d������w�y����y�b���������UG4"/L\��^g7�z7c����� j~Db]q���+��?T �UO���PZx��|��qL(���d�Dv`R#���
����S+������e{�~8���k}�9/�MN�$�Y_���q�+#�-onx�%Zt�s�A��e������#L�������V������-�<,f�(���}(b�$�� ~S[�|n��~N�c������������32�A5A� #��5P�5����U&����{7��w� �����FI����4��&?M��pV�&��E/p�Tm��'������2AM�����'u�MI�|>�h�����Cgd�!�|>����\�x>�yEy�r�Ds��,P������w|�������H����X�pH��[��agGNj;���������H?�u"��� I�sY{�$y90fG��sl��5�J�a:��~L�+�g���1�4F���,����k���!f�yD��OD>p���G\���B��;3��H�� �^0����<B�%��[�/��W�������dv&���v|�K�up���I�����A�7����9�z~��n��@h�������7������1�e��S�����+}qz��R��3��U�#.@o�[�V�-����e�������[�*���K����U���M���-A}iLB]����?dE���z�t���R��1n����)�!��X��
���6��_1��#�#��"��M�DN_�s��I�Y�T���d��ij�9���Zc�
��~b��� I�Ln�Q�[����yr'���_d��pFDD��8#���Y�o����3a�}0�X�#>A��)I��N�D������K �����=���8@>���+���XB%���,
��������wx~���<8>���@���p}-�~�HD ���$�I����<�OLJI4�c���v���)-J���o�$�;�N�Utn��36��m��S�d^X|� n�*�Q�0{��(fc�=|�Cl��p�Z��$2��s��DT�
�r���f_�D���a�5b�s���{!]��*������F�8`�p�����7p�����|\0���btG�-xwr������2�0hQ�P��*�����;��6F�1*��T/����H�VQ}���/��m���2��<Yy�x��&g;1�T�L��y'utO7�=����W�"�q�}��s�46��P�{���:���K�W�� �W�9Az�� %G��>$�ZZ`y�u'�#���+{a���`I���Y�� WR��bJ:1m�w%���c:���~�;��s*����ZC)�et�EC����0�>]/<
��i�s�t'+���Y)g=�N���0�Q�2�����\!`�m����Y�QE��%#?��h�H+��1��BN^�lC�Hg��B���%�F#
���n�V�#6�~�����v/y�����?�,��#���E$�7��S"�h�������\Dt��H����^��nNlw��u*���������R�����������d�&m_6�����&�&�����CB�k��r~����
�u�(������w��'�W����>X,�LG]����<~��s��W����������2[sW��\��>��T���KM]�k��_f�*�^��_��!�h��a�������6F� Y�/25�-��H��HS��
9
�J��`8d;r��V��I���U��}�
y@�A2��cK h;i���nhoZ�Y��SN�I�8a��0�|� �t�(�-l�����/��L������Z����������o%�cb��7���a[�~>K=�F��r���$�p��$�t����B��Bi�}Y#]���mX3���V��]A����46�X��S����*;�����[m�R�\w��������~��&�(����f��:��o�.�G�
�t�Jm�@�����/�D�.� ��K_F�LfM�;�W|��Q�a�
���JQ�p4]�����-3;�����7�x��&},�>G.R�_��_d
]]���4����*��q+�����jT�ow[m �����[����M�)��,�����h�F�E���z'
/�RH���57�G���(v�1F(��d,'�f~���\��������:��t�g��Jh�g����J���%7]bO1�A� |w���+:j������v���)'�7K�d�5#��|i ��v���@�g�n�����_r�V�G3��7��
}���I��v���d���*�s�U��K�&�:��F�p�T��i�as� 3�)�����d��Qf�� ,I�N�t8���Lxm����UM`�Wo���0 �m6�X�Ba��t��m������\�����M�U�G��Ps�|�P|�p����=u���dt�B��o\A�ugES/B06���<��*�$W8C��)�,��@��R�`�cG��oq�����2`D
�<%t����0>��q���R��:�`v�GO��w~�Q�NP�_�s�?�i&�] �JY����dg
�$������n+���8�Bj��ev����$W
JI��_G}�"Do��@u���:tU�!�Eew�a$�b�"���q����~�k��5w������C*�@�J��!�����
-����9c�`���e<���Q54�����M�����E:��P��Y�"����b>����_/����������,;�gQ����=��s�'3�G���� ��� H��!����$��Z���j��/<o����g�*7�5a��m����#��y��A9��Gi�F��]�� 9���Tk�^�� ���d,��a"��3�[�=��7�Jd��y��L{j]h6d�5R�_7be���f&���s\�q���Z^�.�[��0���������X4�:�
'���pY�Vj ~Vj���/��-���,�r��s��ja�s�����C� O�� N��K�����D}QsG����F����xhjkmc?[(�p2b.�O���d9�"��0��$r�����R�0<�t ��{��B;$��z�)/�&�8��B���n������.wprr~��\�,V�������:�r�\�$O��������dv��o���H�&0�"���}r��&- /����Tv�z�����L�H��
�m]�M\nk)4�p��
���U��#[��cO��:���������B
h'X�w�'�����-�_vg����M��R~�m��S6�(���I2���g���N�P��2@� �*�L�,#����6l6�u-/09��eX��nk�w��t���%������t���b*�@$8��*��uY"r����I�]C�dj���_��5z��%X,Ngtq3��I9�3�pN(��yQsVl5t�����dsY��b�b
$b����-�:Q�N.>{)��D<W���Ip���f��d��X_<�aco2����AhR���x�a���4��x�I%Zv@-pl-��S��O���'(N��F-�^��^���7��!�#-����,�:�sa����_1XG�]��p���I[���W^u�'-�'�bp��+�������'c��^�)UX���]��+�re#������SR�L�$�yx#���[����P�|�������1�K��}�7��>����'iA3�n4��6�H����2&;�JF���\v,����������Q7�1�3���hR��\H/�p@t|n�L��8KYt��<b��O�����P��#(�c�����j!5�/%��O�F����i8����@�26dm��ZW9}N�����s����O�B�h��C�X�@I�b���K��L.��]1z0�aE�Q�2���NA��9�T��0$�5
'ET4tzf{�UL�<�^�������G�0Q*%�8�3���87s;X������Dyr�X�����1�H=]x����V�mX/JF������zW[��C
����*���.e(�r��S�Z�6���Nc�w�QD�r6jG���D�s��=9������_�$��u ��C�[�3 ��
�/����,�Y�|��&�!�%����������H�4���g�]�w0�IJp��EH��$Zt��o�x�sk8�����������|oQSRR$�����=��v_7���"��>\���KqUdg���s�
vk~�Z^b�P�je=�� ��
�m�`gF�l�1�K��tO��0��p_��H�Q�T|����>S-Wq f.�z�
�jFr:)W)��r�/P����� tp p@:��R���L�P�O��;y,V�3�wSO.��i\R�Re�(��^��)��TQ�4�4���a�*D������2,��������K0cJ*bc[�q_B�.��s2"�1C-��?H��(���A0])4����?k����+�����SBU�����Ecr�a��e2��\��2��0����~6e���7&��
&��y�!R!lem%��^����~���Ip� w~��Z���F��������gd��:���8����%4�����Y�� �i������'V��4��&��[�u������}�?S�!�7�?�Eg}�B!��f����YMzp�T>����N$�a���Y�P�`%u���A�aU
�a�7T2=H���Z�o�8��������[�l!6a�">��'| �x�&��p�� n����43��O��&����Z�t�i�sZ���#���" %�����'M���d�,?����(������olE�� ����[y����nF�d
����"��(Q��bIv�K2�)�k��Y�
��02&��b�a�(�U5C�S*��ZS��m�����w#M�_ E|Ud�����^��A�%��4a���Z/2<=����;�-9� ����u�t����������Y7�e��T(E�aE^�k ���Zlx��r������[7��[@��� �b���q8�����D�$��������1�vV�is8�h���4*
W^x�b���!;�^?x�tp ���6\���s84�����`.0�����Ug�P� �\#��#��,��!B�r�����NnQ��`����,Dc�lb�����,����_��5pn�z���*C����6L��>9�������+�=n@*�1���
�*�R�����^0A2������~�sU�
��f��@e�7�+r��,QI��]4@�);�������)����m9����&����4�l�aZ�;`�e<��gB:�������9l�����P����������A�����QB����i���H
���% ����$����O���)|~n{a��WG���0����
Lp�ee����)��%B(K�fA|jD�
��.��,�5���G{�h|A���%C6��~9x}����r���{z<��UczZ
�9��%^������hG���f�������5"���I���� }����Ji�d��Y�dA��������Jm��~d�L���Wi�Uw]y�������������)���y�"��`���m�^������S~��})Y~N�O�O�O��������������m�]n���!��r�'�,zxr�V�p��x<�H��dY,Z�����o�m��}?���y���,
�1�SL7x4:���`�@��&h�S���Q������\x�H�V�}��������am�w���h����uz�.���e�gL��it�}^o��zKe�63c���$�9���q��J�J�M"��0B����A�b3=�T�6�^E������^yo{����U������G�|��ALA8���?�fa��8�"x��2p�g� �������{����������J1���mq�F��{~r@����q�����������p��6
u�H3�Y���l&�h6e2q��P�{���:b��'-U����C.����?`+���5;���N����8yY����������>�����4\���ry�.�t�&��u��n���+Z��������8�I���{���s�@���oV���'JtQ��l>����c�]B��~�,���?;�/������<���@��]���W
mv1��L���(�����PXtg���7��c��)�`�BN���X��`U�:2X��%,?�7��g��� ��rL�=�9�)b��Zr�]A�A�.�������yxw�y r'���"�I����2�.�����d0'����;Fx��6\�����#mI�����fK��ya���Vt�����3���u���#��[����7�����_����2 �3�w�o�V� I�\ER�g�I����$�n";�*������?;�Z����wI���M}t���7v�>q�
�#��v���rY�����-�~<z����Y�N4$D�z}0huG|��S_� ����Cjru��
)�I������*��,�d ��J3]���*���Nk-_���m������UTH�&���Fl���=0�����tq�+$� �n���e���y[|�:���E�F�\h������/���Ev��E&����.&xK$^#���Q���8 ��{���4k��LLZX���w���{~)UF���+D�TV"m+(!XI����`���,��t����|P*�����������,cta��c�����:>9��_��>�'6�i5��|�����}�HLU��y�q��0��r�9m�L��!i�Z��+��ej�_��m>���O��J�^`��W�i.������E>�_L��gZ1����j��Zq`��c�t�J�h����S�!R�5@��5w
R�r5 /C��[m Q,� I&E<b{�I_^�on��o�E���
y�����X�*��je��X����a�z6�{��JW�Z������g4���<$/1����w���$LfC~�R��E#����0
�r�t��8L��8��_��"�{��%�7���Qo��v�> �D�!�s No����e���]���[���t�)���S���2xM+
����YF��0.�z�E���$���D���:�����,������J��W�^���sr�#�����[&,m��`2IH�'4���L��5^K��U[_�S��x���
mpO���q��+����n���b���O�}w��I���qsz���p����������[�q����u��j�g�'��"���j�}bSf�w����*F3��p��
{~:���$�������P��m"�������|i��Cs�li�>�|�'��,S��\Z�� ��l�=F��*hW[0����Ue����r��:�9cL��������vp"�����a��t~:�<;>{� ��M�
��G�{��.���x��";����%��g������CO^�?���������M��P�
�����x
��d:8��~�����U�����-R�R�d?}/��p�X�W^�s�R�g2�N�����]}�`C(L���N�;���8��;����������?<y�E�������l4" 8�F�{����_NB����wh��d�
������x�����4<���s'iXn�������f����Q��
#p����V
R�)B�Y������s\o��3�W������['J\���Jl�(�P���1�� 7��E���4}<��1:Ce�h���S��O���hi���;�B�z��� ;�1�5���W��vaU�q0F��;J�t�@�MK��X?t��LL� �W*����:X:��}�Z�!/�@u�J�[-�N��k9
u��$%t^��b��LF}��)-��vt������uM5����S����/������:,2�i�
����p��������5���H�.��<0%����}|+�>�'��$ �Or�����c*�'
7]� ����H���C9J?��3S�H8G�����1��T��"�"zvF&'