I believe I fixed all flaws mentioned so far (see attachment). Also I did a new benchmark to make sure that new patch makes PostgreSQL work faster and doesn't cause performance degradation in some cases.
"usual pgbench" row corresponds to `pgbench -j 8 -c 8 -T 30 pgbench` performed on a 4-core PC. "N partitions" rows correspond to a benchmark described in a first message of this thread performed on the same PC. N is and argument given to gen.pl script i.e. number of partitions in generated partitioned table. Here are results (3 tests, TPS excluding connections establishing): Test | Before | After | Delta avg ----------------|-----------|-----------|------------- | 295.7 | 295.0 | usual pgbench | 303.1 | 299.6 | ~ 0% | 297.7 | 302.7 | ----------------|-----------|-----------|------------- | 28022.3 | 27956.1 | 10 partitions | 27550.1 | 28916.9 | ~ 0% | 28617.0 | 28022.9 | ----------------|-----------|-----------|------------- | 3021.4 | 3184.0 | 100 partitions | 2949.1 | 3120.1 | 3% more TPS | 2870.6 | 2825.2 | ----------------|-----------|-----------|------------- | 106.7 | 158.6 | 1000 partitions | 105.2 | 168.4 | 53% more TPS | 105.9 | 162.0 | On Fri, 18 Dec 2015 13:39:01 -0500 Tom Lane <t...@sss.pgh.pa.us> wrote: > Robert Haas <robertmh...@gmail.com> writes: > > I don't know, I'm still not very comfortable with this. And Tom > > didn't like dictating that hash_any() must be no-fail, though I'm > > not sure why. > > What I definitely didn't like was assuming at a distance that it would > be no-fail. If we're to depend on that, the patch had better attach > a comment saying so to the header comments of the function(s) it's > assuming that about. Otherwise, somebody could hack up hashfunc.c > in a way that breaks the assumption, without any clue that some code > in a very-far-away module is critically reliant on it. > > > Let's wait to see what others think. > > A few observations: > > * This bit is too cute by half, if not three-quarters: > > + uint32 itemsizelg:2; /* sizeof one > item log 2 */ > + uint32 capacity:30; /* capacity of > array */ > > Is there a good reason to assume that the only things we'll ever store > in these arrays are of size no more than 8 bytes? Are we so desperate > to save space that we cannot spare two separate words for itemsize and > capacity? (ISTM it's a good bet that the extra code for accessing > these bitfields occupies more space than would be saved, considering > how few ResourceOwners typically exist at one time.) Let's just make > it a couple of ints and be done. Actually, maybe nitems and capacity > should be size_t, just in case. > > * An alternative design would be to forget itemsizelg altogether and > insist that everything stored in the resource arrays be a Datum, > which could then be coerced to/from some form of integer or some form > of pointer as appropriate. That would waste some space in the int > case, but it would considerably simplify both the ResourceArray code > and the APIs to it, which might be worth the price of assuming we'll > never store anything bigger than 8 bytes. It also would make this > look more like some existing APIs such as the on_exit callbacks. > > * A lot of the code churn comes from the insistence on defining > callbacks, which I'm dubious that we need. We could instead have a > function that is "get any convenient one of the array elements" and > revise the loops in ResourceOwnerReleaseInternal to be like > > while ((item = getconvenientitem(resourcearray))) > { > drop item in exactly the same way as before > } > > I find that preferable to the proposed ResourceArrayRemoveAll > > + while (resarr->nitems > 0) > + { > + releasecb(resarr->itemsarr, isCommit); > + } > > which certainly looks like it's an infinite loop; it's assuming (again > with no documentation) that the callback function will cause the array > to get smaller somehow. With the existing coding, it's much more > clear why we think the loops will terminate. > > * The reason that ResourceOwnerReleaseInternal was not horribly > inefficient was that its notion of "any convenient one" of the items > to be deleted next was in fact the one that the corresponding Forget > function would examine first, thus avoiding an O(N^2) cost to > re-identify the item to be dropped. I think we should make an effort > to be more explicit about that connection in any rewrite. In > particular, it looks to me like when a hash array is in use, things > will get slower not faster because we'll be adding a hash lookup step > to each forget operation. Maybe we should consider adjusting the > APIs so that that can be avoided. Or possibly we could have internal > state in the ResourceArrays that says "we expect this item to be > dropped in a moment, check that before going to the trouble of a hash > lookup". > > * Actually, I'm not convinced that the proposed reimplementation of > ResourceArrayRemove isn't horribly slow much of the time. It sure > looks like it could degrade to a linear search very easily. > > * I still say that the assumption embodied as > RESOURCE_ARRAY_ZERO_ELEMENT (ie that no valid entry is all-zero-bits) > is pretty unacceptable. It might work for pointers, but I don't like > it for resources represented by integer indexes. > > regards, tom lane > >
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index 9ee654e..5a00a03 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS) * of 2. There is no need to do mod a prime (mod is sooo slow!). * If you need less than 32 bits, use a bitmask. * + * This procedure never fails. Its important. Some code notably ResourceOwner + * relies on this. + * * Note: we could easily change this function to return a 64-bit hash value * by using the final values of both b and c. b is perhaps a little less * well mixed than c, however. diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c index 0e7acbf..abed36c 100644 --- a/src/backend/utils/resowner/resowner.c +++ b/src/backend/utils/resowner/resowner.c @@ -29,6 +29,219 @@ #include "utils/snapmgr.h" /* + * ResourceArray is a common structure for storing different types of resources. + * + * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For + * each resource type there are procedures ResourceOwnerRemember* and + * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures + * which should be called before corresponding ResourceOwnerRemember* calls + * (see below). Internally each type of resource is stored in separate + * ResourceArray. + * + * There are two major reasons for using ResourceArray instead of, say, + * regular C arrays. + * + * Firstly we would like to prevent code duplication. For instance + * ResourceArray provides generic Remember/Forget/Enlarge procedures, so + * corresponding ResourceOwner* procedures are just a typesafe wrappers for + * these procedures. + * + * Secondly ResourceArray must be more efficient than regular C array. + * Current implementation in general could be considered a hash table. It has + * O(1) complexity of both Remember and Forget procedures. + */ +typedef struct ResourceArray +{ + Datum *itemsarr; /* buffer for storing values */ + Datum invalidval; /* value that is considered invalid */ + uint32 capacity; /* capacity of array */ + uint32 nitems; /* how many items is stored in items array */ + uint32 maxitems; /* precalculated RESARRAY_MAX_ITEMS(capacity) */ + uint32 lastidx; /* index of last item returned by GetAny */ +} ResourceArray; + +/* + * This number is used as initial size of resource array. If given number of + * items is not enough, we double array size and reallocate memory. + * + * Should be power of two since we use (arrsize - 1) as mask for hash value. + * + */ +#define RESARRAY_INIT_SIZE 16 + +/* + * How many items could be stored in a resource array of given capacity. If + * this number is reached we need to resize an array to prevent hash collisions. + * + * This computation actually costs only two additions and one binary shift. + */ +#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4) + +/* + * Initialize ResourceArray + */ +static void +ResourceArrayInit(ResourceArray * resarr, Datum invalidval) +{ + Assert(resarr->itemsarr == NULL); + Assert(resarr->capacity == 0); + Assert(resarr->nitems == 0); + Assert(resarr->maxitems == 0); + Assert(resarr->invalidval == 0); + Assert(resarr->lastidx == 0); + + resarr->invalidval = invalidval; +} + +/* + * Add a resource to ResourceArray + * + * Caller must have previously done ResourceArrayEnlarge() + */ +static void +ResourceArrayAdd(ResourceArray * resarr, Datum data) +{ + Datum idx; + Datum mask = resarr->capacity - 1; + + Assert(resarr->maxitems > resarr->nitems); + Assert(resarr->capacity > 0); + Assert(resarr->itemsarr != NULL); + Assert(data != resarr->invalidval); + + idx = hash_any((void *) &data, sizeof(data)) & mask; + + while (true) + { + if (resarr->itemsarr[idx] == resarr->invalidval) + break; + idx = (idx + 1) & mask; + } + + resarr->itemsarr[idx] = data; + resarr->nitems++; +} + +/* + * Remove a resource from ResourceArray + * + * Returns true on success, false if resource was not found + */ +static bool +ResourceArrayRemove(ResourceArray * resarr, Datum data) +{ + uint32 i; + Datum idx; + Datum mask = resarr->capacity - 1; + + Assert(resarr->capacity > 0); + Assert(resarr->itemsarr != NULL); + Assert(data != resarr->invalidval); + + idx = hash_any((void *) &data, sizeof(data)) & mask; + for (i = 0; i < resarr->capacity; i++) + { + if (resarr->itemsarr[idx] == data) + { + resarr->itemsarr[idx] = resarr->invalidval; + resarr->nitems--; + return true; + } + idx = (idx + 1) & mask; + } + + return false; +} + +/* + * Make sure there is a room for at least one more resource in an array. + * + * This is separate from actually inserting a resource because if we run out + * of memory, it's critical to do so *before* acquiring the resource. + */ +static void +ResourceArrayEnlarge(ResourceArray * resarr) +{ + uint32 i, + oldcap; + Datum *olditemsarr; + + if (resarr->nitems < resarr->maxitems) + return; /* nothing to do */ + + olditemsarr = resarr->itemsarr; + oldcap = resarr->capacity; + + resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE; + resarr->itemsarr = (Datum *) + MemoryContextAlloc(TopMemoryContext, + resarr->capacity * sizeof(Datum)); + resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity); + resarr->nitems = 0; + + for (i = 0; i < resarr->capacity; i++) + resarr->itemsarr[i] = resarr->invalidval; + + if (olditemsarr != NULL) + { + while (oldcap > 0) + { + oldcap--; + if (olditemsarr[oldcap] != resarr->invalidval) + ResourceArrayAdd(resarr, olditemsarr[oldcap]); + } + pfree(olditemsarr); + } +} + +/* + * Get any convenient element. + * + * Returns true on success, false on failure. + */ +static bool +ResourceArrayGetAny(ResourceArray * resarr, Datum *out) +{ + uint32 mask; + + if (resarr->nitems == 0) + return false; + + Assert(resarr->capacity > 0); + mask = resarr->capacity - 1; + + for (;;) + { + resarr->lastidx = resarr->lastidx & mask; + if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval) + break; + + resarr->lastidx++; + } + + *out = resarr->itemsarr[resarr->lastidx]; + return true; +} + +/* + * Return ResourceArray to initial state + */ +static void +ResourceArrayFree(ResourceArray * resarr) +{ + Assert(resarr->nitems == 0); + + resarr->capacity = 0; + resarr->maxitems = 0; + + if (!resarr->itemsarr) + return; + + pfree(resarr->itemsarr); + resarr->itemsarr = NULL; +} + +/* * To speed up bulk releasing or reassigning locks from a resource owner to * its parent, each resource owner has a small cache of locks it owns. The * lock manager has the same information in its local lock hash table, and @@ -56,53 +269,22 @@ typedef struct ResourceOwnerData ResourceOwner nextchild; /* next child of same parent */ const char *name; /* name (just for debugging) */ - /* We have built-in support for remembering owned buffers */ - int nbuffers; /* number of owned buffer pins */ - Buffer *buffers; /* dynamically allocated array */ - int maxbuffers; /* currently allocated array size */ - /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */ int nlocks; /* number of owned locks */ LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */ - /* We have built-in support for remembering catcache references */ - int ncatrefs; /* number of owned catcache pins */ - HeapTuple *catrefs; /* dynamically allocated array */ - int maxcatrefs; /* currently allocated array size */ - - int ncatlistrefs; /* number of owned catcache-list pins */ - CatCList **catlistrefs; /* dynamically allocated array */ - int maxcatlistrefs; /* currently allocated array size */ - - /* We have built-in support for remembering relcache references */ - int nrelrefs; /* number of owned relcache pins */ - Relation *relrefs; /* dynamically allocated array */ - int maxrelrefs; /* currently allocated array size */ - - /* We have built-in support for remembering plancache references */ - int nplanrefs; /* number of owned plancache pins */ - CachedPlan **planrefs; /* dynamically allocated array */ - int maxplanrefs; /* currently allocated array size */ - - /* We have built-in support for remembering tupdesc references */ - int ntupdescs; /* number of owned tupdesc references */ - TupleDesc *tupdescs; /* dynamically allocated array */ - int maxtupdescs; /* currently allocated array size */ - - /* We have built-in support for remembering snapshot references */ - int nsnapshots; /* number of owned snapshot references */ - Snapshot *snapshots; /* dynamically allocated array */ - int maxsnapshots; /* currently allocated array size */ - - /* We have built-in support for remembering open temporary files */ - int nfiles; /* number of owned temporary files */ - File *files; /* dynamically allocated array */ - int maxfiles; /* currently allocated array size */ - - /* We have built-in support for remembering dynamic shmem segments */ - int ndsms; /* number of owned shmem segments */ - dsm_segment **dsms; /* dynamically allocated array */ - int maxdsms; /* currently allocated array size */ + /* We have built-in support for remembering: */ + + ResourceArray catrefarr; /* `HeapTuple`s */ + ResourceArray catlistrefarr; /* `ResourceOwner`s */ + ResourceArray relrefarr; /* `Relation`s */ + ResourceArray planrefarr; /* `CachedPlan*`s */ + ResourceArray tupdescarr; /* `TupleDesc`s */ + ResourceArray snapshotarr; /* `Snapshot`s */ + ResourceArray dsmarr; /* `dsm_segment*`s */ + ResourceArray bufferarr; /* `Buffer`s */ + ResourceArray filearr; /* `File`s */ + } ResourceOwnerData; @@ -168,6 +350,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name) parent->firstchild = owner; } + ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer)); + ResourceArrayInit(&(owner->filearr), (Datum) (-1)); + return owner; } @@ -229,6 +421,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceOwner child; ResourceOwner save; ResourceReleaseCallbackItem *item; + Datum foundres; /* Recurse to handle descendants */ for (child = owner->firstchild; child != NULL; child = child->nextchild) @@ -252,45 +445,34 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, * During a commit, there shouldn't be any remaining pins --- that * would indicate failure to clean up the executor correctly --- so * issue warnings. In the abort case, just clean up quietly. - * - * We are careful to do the releasing back-to-front, so as to avoid - * O(N^2) behavior in ResourceOwnerForgetBuffer(). */ - while (owner->nbuffers > 0) + while (ResourceArrayGetAny(&(owner->bufferarr), &foundres)) { + Buffer res = (Buffer) foundres; + if (isCommit) - PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]); - ReleaseBuffer(owner->buffers[owner->nbuffers - 1]); + PrintBufferLeakWarning(res); + ReleaseBuffer(res); } - /* - * Release relcache references. Note that RelationClose will remove - * the relref entry from my list, so I just have to iterate till there - * are none. - * - * As with buffer pins, warn if any are left at commit time, and - * release back-to-front for speed. - */ - while (owner->nrelrefs > 0) + /* Ditto for relcache references. */ + while (ResourceArrayGetAny(&(owner->relrefarr), &foundres)) { + Relation res = (Relation) foundres; + if (isCommit) - PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]); - RelationClose(owner->relrefs[owner->nrelrefs - 1]); + PrintRelCacheLeakWarning(res); + RelationClose(res); } - /* - * Release dynamic shared memory segments. Note that dsm_detach() - * will remove the segment from my list, so I just have to iterate - * until there are none. - * - * As in the preceding cases, warn if there are leftover at commit - * time. - */ - while (owner->ndsms > 0) + /* Ditto for dynamic shared memory segments */ + while (ResourceArrayGetAny(&(owner->dsmarr), &foundres)) { + dsm_segment *res = (dsm_segment *) foundres; + if (isCommit) - PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]); - dsm_detach(owner->dsms[owner->ndsms - 1]); + PrintDSMLeakWarning(res); + dsm_detach(res); } } else if (phase == RESOURCE_RELEASE_LOCKS) @@ -351,47 +533,63 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, * As with buffer pins, warn if any are left at commit time, and * release back-to-front for speed. */ - while (owner->ncatrefs > 0) + while (ResourceArrayGetAny(&(owner->catrefarr), &foundres)) { + HeapTuple res = (HeapTuple) foundres; + if (isCommit) - PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]); - ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]); + PrintCatCacheLeakWarning(res); + ReleaseCatCache(res); } + /* Ditto for catcache lists */ - while (owner->ncatlistrefs > 0) + while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres)) { + CatCList *res = (CatCList *) foundres; + if (isCommit) - PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]); - ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]); + PrintCatCacheListLeakWarning(res); + ReleaseCatCacheList(res); } + /* Ditto for plancache references */ - while (owner->nplanrefs > 0) + while (ResourceArrayGetAny(&(owner->planrefarr), &foundres)) { + CachedPlan *res = (CachedPlan *) foundres; + if (isCommit) - PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]); - ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true); + PrintPlanCacheLeakWarning(res); + ReleaseCachedPlan(res, true); } + /* Ditto for tupdesc references */ - while (owner->ntupdescs > 0) + while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres)) { + TupleDesc res = (TupleDesc) foundres; + if (isCommit) - PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]); - DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]); + PrintTupleDescLeakWarning(res); + DecrTupleDescRefCount(res); } + /* Ditto for snapshot references */ - while (owner->nsnapshots > 0) + while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres)) { + Snapshot res = (Snapshot) foundres; + if (isCommit) - PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]); - UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]); + PrintSnapshotLeakWarning(res); + UnregisterSnapshot(res); } /* Ditto for temporary files */ - while (owner->nfiles > 0) + while (ResourceArrayGetAny(&(owner->filearr), &foundres)) { + File res = (File) foundres; + if (isCommit) - PrintFileLeakWarning(owner->files[owner->nfiles - 1]); - FileClose(owner->files[owner->nfiles - 1]); + PrintFileLeakWarning(res); + FileClose(res); } /* Clean up index scans too */ @@ -418,16 +616,7 @@ ResourceOwnerDelete(ResourceOwner owner) Assert(owner != CurrentResourceOwner); /* And it better not own any resources, either */ - Assert(owner->nbuffers == 0); Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1); - Assert(owner->ncatrefs == 0); - Assert(owner->ncatlistrefs == 0); - Assert(owner->nrelrefs == 0); - Assert(owner->ndsms == 0); - Assert(owner->nplanrefs == 0); - Assert(owner->ntupdescs == 0); - Assert(owner->nsnapshots == 0); - Assert(owner->nfiles == 0); /* * Delete children. The recursive call will delink the child from me, so @@ -444,25 +633,15 @@ ResourceOwnerDelete(ResourceOwner owner) ResourceOwnerNewParent(owner, NULL); /* And free the object. */ - if (owner->buffers) - pfree(owner->buffers); - if (owner->catrefs) - pfree(owner->catrefs); - if (owner->catlistrefs) - pfree(owner->catlistrefs); - if (owner->relrefs) - pfree(owner->relrefs); - if (owner->planrefs) - pfree(owner->planrefs); - if (owner->tupdescs) - pfree(owner->tupdescs); - if (owner->snapshots) - pfree(owner->snapshots); - if (owner->files) - pfree(owner->files); - if (owner->dsms) - pfree(owner->dsms); - + ResourceArrayFree(&(owner->catrefarr)); + ResourceArrayFree(&(owner->catlistrefarr)); + ResourceArrayFree(&(owner->relrefarr)); + ResourceArrayFree(&(owner->planrefarr)); + ResourceArrayFree(&(owner->tupdescarr)); + ResourceArrayFree(&(owner->snapshotarr)); + ResourceArrayFree(&(owner->dsmarr)); + ResourceArrayFree(&(owner->bufferarr)); + ResourceArrayFree(&(owner->filearr)); pfree(owner); } @@ -575,26 +754,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg) void ResourceOwnerEnlargeBuffers(ResourceOwner owner) { - int newmax; - - if (owner == NULL || - owner->nbuffers < owner->maxbuffers) - return; /* nothing to do */ - - if (owner->buffers == NULL) - { - newmax = 16; - owner->buffers = (Buffer *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer)); - owner->maxbuffers = newmax; - } - else - { - newmax = owner->maxbuffers * 2; - owner->buffers = (Buffer *) - repalloc(owner->buffers, newmax * sizeof(Buffer)); - owner->maxbuffers = newmax; - } + if (owner == NULL) + return; + ResourceArrayEnlarge(&(owner->bufferarr)); } /* @@ -608,12 +770,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner) void ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer) { - if (owner != NULL) - { - Assert(owner->nbuffers < owner->maxbuffers); - owner->buffers[owner->nbuffers] = buffer; - owner->nbuffers++; - } + if (owner == NULL) + return; + ResourceArrayAdd(&(owner->bufferarr), (Datum) buffer); } /* @@ -625,33 +784,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer) void ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer) { - if (owner != NULL) - { - Buffer *buffers = owner->buffers; - int nb1 = owner->nbuffers - 1; - int i; + bool res; - /* - * Scan back-to-front because it's more likely we are releasing a - * recently pinned buffer. This isn't always the case of course, but - * it's the way to bet. - */ - for (i = nb1; i >= 0; i--) - { - if (buffers[i] == buffer) - { - while (i < nb1) - { - buffers[i] = buffers[i + 1]; - i++; - } - owner->nbuffers = nb1; - return; - } - } + if (owner == NULL) + return; + + res = ResourceArrayRemove(&(owner->bufferarr), (Datum) buffer); + if (!res) elog(ERROR, "buffer %d is not owned by resource owner %s", buffer, owner->name); - } } /* @@ -667,6 +808,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer) void ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock) { + Assert(locallock != NULL); + if (owner->nlocks > MAX_RESOWNER_LOCKS) return; /* we have already overflowed */ @@ -714,25 +857,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock) void ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner) { - int newmax; - - if (owner->ncatrefs < owner->maxcatrefs) - return; /* nothing to do */ - - if (owner->catrefs == NULL) - { - newmax = 16; - owner->catrefs = (HeapTuple *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple)); - owner->maxcatrefs = newmax; - } - else - { - newmax = owner->maxcatrefs * 2; - owner->catrefs = (HeapTuple *) - repalloc(owner->catrefs, newmax * sizeof(HeapTuple)); - owner->maxcatrefs = newmax; - } + ResourceArrayEnlarge(&(owner->catrefarr)); } /* @@ -743,9 +868,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner) void ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple) { - Assert(owner->ncatrefs < owner->maxcatrefs); - owner->catrefs[owner->ncatrefs] = tuple; - owner->ncatrefs++; + ResourceArrayAdd(&(owner->catrefarr), (Datum) tuple); } /* @@ -754,25 +877,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple) void ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple) { - HeapTuple *catrefs = owner->catrefs; - int nc1 = owner->ncatrefs - 1; - int i; + bool res = ResourceArrayRemove(&(owner->catrefarr), + (Datum) tuple); - for (i = nc1; i >= 0; i--) - { - if (catrefs[i] == tuple) - { - while (i < nc1) - { - catrefs[i] = catrefs[i + 1]; - i++; - } - owner->ncatrefs = nc1; - return; - } - } - elog(ERROR, "catcache reference %p is not owned by resource owner %s", - tuple, owner->name); + if (!res) + elog(ERROR, "catcache reference %p is not owned by resource owner %s", + tuple, owner->name); } /* @@ -785,25 +895,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple) void ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner) { - int newmax; - - if (owner->ncatlistrefs < owner->maxcatlistrefs) - return; /* nothing to do */ - - if (owner->catlistrefs == NULL) - { - newmax = 16; - owner->catlistrefs = (CatCList **) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *)); - owner->maxcatlistrefs = newmax; - } - else - { - newmax = owner->maxcatlistrefs * 2; - owner->catlistrefs = (CatCList **) - repalloc(owner->catlistrefs, newmax * sizeof(CatCList *)); - owner->maxcatlistrefs = newmax; - } + ResourceArrayEnlarge(&(owner->catlistrefarr)); } /* @@ -814,9 +906,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner) void ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list) { - Assert(owner->ncatlistrefs < owner->maxcatlistrefs); - owner->catlistrefs[owner->ncatlistrefs] = list; - owner->ncatlistrefs++; + ResourceArrayAdd(&(owner->catlistrefarr), (Datum) list); } /* @@ -825,25 +915,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list) void ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list) { - CatCList **catlistrefs = owner->catlistrefs; - int nc1 = owner->ncatlistrefs - 1; - int i; + bool res = ResourceArrayRemove(&(owner->catlistrefarr), + (Datum) list); - for (i = nc1; i >= 0; i--) - { - if (catlistrefs[i] == list) - { - while (i < nc1) - { - catlistrefs[i] = catlistrefs[i + 1]; - i++; - } - owner->ncatlistrefs = nc1; - return; - } - } - elog(ERROR, "catcache list reference %p is not owned by resource owner %s", - list, owner->name); + if (!res) + elog(ERROR, "catcache list reference %p is not owned by resource owner %s", + list, owner->name); } /* @@ -856,25 +933,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list) void ResourceOwnerEnlargeRelationRefs(ResourceOwner owner) { - int newmax; - - if (owner->nrelrefs < owner->maxrelrefs) - return; /* nothing to do */ - - if (owner->relrefs == NULL) - { - newmax = 16; - owner->relrefs = (Relation *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation)); - owner->maxrelrefs = newmax; - } - else - { - newmax = owner->maxrelrefs * 2; - owner->relrefs = (Relation *) - repalloc(owner->relrefs, newmax * sizeof(Relation)); - owner->maxrelrefs = newmax; - } + ResourceArrayEnlarge(&(owner->relrefarr)); } /* @@ -885,9 +944,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner) void ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel) { - Assert(owner->nrelrefs < owner->maxrelrefs); - owner->relrefs[owner->nrelrefs] = rel; - owner->nrelrefs++; + ResourceArrayAdd(&(owner->relrefarr), (Datum) rel); } /* @@ -896,25 +953,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel) void ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel) { - Relation *relrefs = owner->relrefs; - int nr1 = owner->nrelrefs - 1; - int i; + bool res = ResourceArrayRemove(&(owner->relrefarr), + (Datum) rel); - for (i = nr1; i >= 0; i--) - { - if (relrefs[i] == rel) - { - while (i < nr1) - { - relrefs[i] = relrefs[i + 1]; - i++; - } - owner->nrelrefs = nr1; - return; - } - } - elog(ERROR, "relcache reference %s is not owned by resource owner %s", - RelationGetRelationName(rel), owner->name); + if (!res) + elog(ERROR, "relcache reference %s is not owned by resource owner %s", + RelationGetRelationName(rel), owner->name); } /* @@ -937,25 +981,7 @@ PrintRelCacheLeakWarning(Relation rel) void ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner) { - int newmax; - - if (owner->nplanrefs < owner->maxplanrefs) - return; /* nothing to do */ - - if (owner->planrefs == NULL) - { - newmax = 16; - owner->planrefs = (CachedPlan **) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *)); - owner->maxplanrefs = newmax; - } - else - { - newmax = owner->maxplanrefs * 2; - owner->planrefs = (CachedPlan **) - repalloc(owner->planrefs, newmax * sizeof(CachedPlan *)); - owner->maxplanrefs = newmax; - } + ResourceArrayEnlarge(&(owner->planrefarr)); } /* @@ -966,9 +992,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner) void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan) { - Assert(owner->nplanrefs < owner->maxplanrefs); - owner->planrefs[owner->nplanrefs] = plan; - owner->nplanrefs++; + ResourceArrayAdd(&(owner->planrefarr), (Datum) plan); } /* @@ -977,25 +1001,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan) void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan) { - CachedPlan **planrefs = owner->planrefs; - int np1 = owner->nplanrefs - 1; - int i; + bool res = ResourceArrayRemove(&(owner->planrefarr), + (Datum) plan); - for (i = np1; i >= 0; i--) - { - if (planrefs[i] == plan) - { - while (i < np1) - { - planrefs[i] = planrefs[i + 1]; - i++; - } - owner->nplanrefs = np1; - return; - } - } - elog(ERROR, "plancache reference %p is not owned by resource owner %s", - plan, owner->name); + if (!res) + elog(ERROR, "plancache reference %p is not owned by resource owner %s", + plan, owner->name); } /* @@ -1017,25 +1028,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan) void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner) { - int newmax; - - if (owner->ntupdescs < owner->maxtupdescs) - return; /* nothing to do */ - - if (owner->tupdescs == NULL) - { - newmax = 16; - owner->tupdescs = (TupleDesc *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc)); - owner->maxtupdescs = newmax; - } - else - { - newmax = owner->maxtupdescs * 2; - owner->tupdescs = (TupleDesc *) - repalloc(owner->tupdescs, newmax * sizeof(TupleDesc)); - owner->maxtupdescs = newmax; - } + ResourceArrayEnlarge(&(owner->tupdescarr)); } /* @@ -1046,9 +1039,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner) void ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc) { - Assert(owner->ntupdescs < owner->maxtupdescs); - owner->tupdescs[owner->ntupdescs] = tupdesc; - owner->ntupdescs++; + ResourceArrayAdd(&(owner->tupdescarr), (Datum) tupdesc); } /* @@ -1057,25 +1048,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc) void ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc) { - TupleDesc *tupdescs = owner->tupdescs; - int nt1 = owner->ntupdescs - 1; - int i; + bool res = ResourceArrayRemove(&(owner->tupdescarr), + (Datum) tupdesc); - for (i = nt1; i >= 0; i--) - { - if (tupdescs[i] == tupdesc) - { - while (i < nt1) - { - tupdescs[i] = tupdescs[i + 1]; - i++; - } - owner->ntupdescs = nt1; - return; - } - } - elog(ERROR, "tupdesc reference %p is not owned by resource owner %s", - tupdesc, owner->name); + if (!res) + elog(ERROR, "tupdesc reference %p is not owned by resource owner %s", + tupdesc, owner->name); } /* @@ -1099,25 +1077,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc) void ResourceOwnerEnlargeSnapshots(ResourceOwner owner) { - int newmax; - - if (owner->nsnapshots < owner->maxsnapshots) - return; /* nothing to do */ - - if (owner->snapshots == NULL) - { - newmax = 16; - owner->snapshots = (Snapshot *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot)); - owner->maxsnapshots = newmax; - } - else - { - newmax = owner->maxsnapshots * 2; - owner->snapshots = (Snapshot *) - repalloc(owner->snapshots, newmax * sizeof(Snapshot)); - owner->maxsnapshots = newmax; - } + ResourceArrayEnlarge(&(owner->snapshotarr)); } /* @@ -1128,9 +1088,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner) void ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) { - Assert(owner->nsnapshots < owner->maxsnapshots); - owner->snapshots[owner->nsnapshots] = snapshot; - owner->nsnapshots++; + ResourceArrayAdd(&(owner->snapshotarr), (Datum) snapshot); } /* @@ -1139,25 +1097,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) void ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot) { - Snapshot *snapshots = owner->snapshots; - int ns1 = owner->nsnapshots - 1; - int i; + bool res = ResourceArrayRemove(&(owner->snapshotarr), + (Datum) snapshot); - for (i = ns1; i >= 0; i--) - { - if (snapshots[i] == snapshot) - { - while (i < ns1) - { - snapshots[i] = snapshots[i + 1]; - i++; - } - owner->nsnapshots = ns1; - return; - } - } - elog(ERROR, "snapshot reference %p is not owned by resource owner %s", - snapshot, owner->name); + if (!res) + elog(ERROR, "snapshot reference %p is not owned by resource owner %s", + snapshot, owner->name); } /* @@ -1182,25 +1127,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot) void ResourceOwnerEnlargeFiles(ResourceOwner owner) { - int newmax; - - if (owner->nfiles < owner->maxfiles) - return; /* nothing to do */ - - if (owner->files == NULL) - { - newmax = 16; - owner->files = (File *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File)); - owner->maxfiles = newmax; - } - else - { - newmax = owner->maxfiles * 2; - owner->files = (File *) - repalloc(owner->files, newmax * sizeof(File)); - owner->maxfiles = newmax; - } + ResourceArrayEnlarge(&(owner->filearr)); } /* @@ -1211,9 +1138,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner) void ResourceOwnerRememberFile(ResourceOwner owner, File file) { - Assert(owner->nfiles < owner->maxfiles); - owner->files[owner->nfiles] = file; - owner->nfiles++; + ResourceArrayAdd(&(owner->filearr), (Datum) file); } /* @@ -1222,25 +1147,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file) void ResourceOwnerForgetFile(ResourceOwner owner, File file) { - File *files = owner->files; - int ns1 = owner->nfiles - 1; - int i; + bool res = ResourceArrayRemove(&(owner->filearr), + (Datum) file); - for (i = ns1; i >= 0; i--) - { - if (files[i] == file) - { - while (i < ns1) - { - files[i] = files[i + 1]; - i++; - } - owner->nfiles = ns1; - return; - } - } - elog(ERROR, "temporery file %d is not owned by resource owner %s", - file, owner->name); + if (!res) + elog(ERROR, "temporary file %d is not owned by resource owner %s", + file, owner->name); } @@ -1265,26 +1177,7 @@ PrintFileLeakWarning(File file) void ResourceOwnerEnlargeDSMs(ResourceOwner owner) { - int newmax; - - if (owner->ndsms < owner->maxdsms) - return; /* nothing to do */ - - if (owner->dsms == NULL) - { - newmax = 16; - owner->dsms = (dsm_segment **) - MemoryContextAlloc(TopMemoryContext, - newmax * sizeof(dsm_segment *)); - owner->maxdsms = newmax; - } - else - { - newmax = owner->maxdsms * 2; - owner->dsms = (dsm_segment **) - repalloc(owner->dsms, newmax * sizeof(dsm_segment *)); - owner->maxdsms = newmax; - } + ResourceArrayEnlarge(&(owner->dsmarr)); } /* @@ -1295,9 +1188,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner) void ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) { - Assert(owner->ndsms < owner->maxdsms); - owner->dsms[owner->ndsms] = seg; - owner->ndsms++; + ResourceArrayAdd(&(owner->dsmarr), (Datum) seg); } /* @@ -1306,26 +1197,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) void ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) { - dsm_segment **dsms = owner->dsms; - int ns1 = owner->ndsms - 1; - int i; + bool res = ResourceArrayRemove(&(owner->dsmarr), + (Datum) seg); - for (i = ns1; i >= 0; i--) - { - if (dsms[i] == seg) - { - while (i < ns1) - { - dsms[i] = dsms[i + 1]; - i++; - } - owner->ndsms = ns1; - return; - } - } - elog(ERROR, - "dynamic shared memory segment %u is not owned by resource owner %s", - dsm_segment_handle(seg), owner->name); + if (!res) + elog(ERROR, "dynamic shared memory segment %u is not owned by resource" + " owner %s", dsm_segment_handle(seg), owner->name); }
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index 9ee654e..5a00a03 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS) * of 2. There is no need to do mod a prime (mod is sooo slow!). * If you need less than 32 bits, use a bitmask. * + * This procedure never fails. Its important. Some code notably ResourceOwner + * relies on this. + * * Note: we could easily change this function to return a 64-bit hash value * by using the final values of both b and c. b is perhaps a little less * well mixed than c, however. diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c index 3330c8d..abed36c 100644 --- a/src/backend/utils/resowner/resowner.c +++ b/src/backend/utils/resowner/resowner.c @@ -37,29 +37,60 @@ * which should be called before corresponding ResourceOwnerRemember* calls * (see below). Internally each type of resource is stored in separate * ResourceArray. + * + * There are two major reasons for using ResourceArray instead of, say, + * regular C arrays. + * + * Firstly we would like to prevent code duplication. For instance + * ResourceArray provides generic Remember/Forget/Enlarge procedures, so + * corresponding ResourceOwner* procedures are just a typesafe wrappers for + * these procedures. + * + * Secondly ResourceArray must be more efficient than regular C array. + * Current implementation in general could be considered a hash table. It has + * O(1) complexity of both Remember and Forget procedures. */ typedef struct ResourceArray { Datum *itemsarr; /* buffer for storing values */ + Datum invalidval; /* value that is considered invalid */ uint32 capacity; /* capacity of array */ uint32 nitems; /* how many items is stored in items array */ + uint32 maxitems; /* precalculated RESARRAY_MAX_ITEMS(capacity) */ + uint32 lastidx; /* index of last item returned by GetAny */ } ResourceArray; /* * This number is used as initial size of resource array. If given number of * items is not enough, we double array size and reallocate memory. + * + * Should be power of two since we use (arrsize - 1) as mask for hash value. + * */ #define RESARRAY_INIT_SIZE 16 /* + * How many items could be stored in a resource array of given capacity. If + * this number is reached we need to resize an array to prevent hash collisions. + * + * This computation actually costs only two additions and one binary shift. + */ +#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4) + +/* * Initialize ResourceArray */ static void -ResourceArrayInit(ResourceArray * resarr) +ResourceArrayInit(ResourceArray * resarr, Datum invalidval) { Assert(resarr->itemsarr == NULL); Assert(resarr->capacity == 0); Assert(resarr->nitems == 0); + Assert(resarr->maxitems == 0); + Assert(resarr->invalidval == 0); + Assert(resarr->lastidx == 0); + + resarr->invalidval = invalidval; } /* @@ -70,11 +101,24 @@ ResourceArrayInit(ResourceArray * resarr) static void ResourceArrayAdd(ResourceArray * resarr, Datum data) { + Datum idx; + Datum mask = resarr->capacity - 1; + + Assert(resarr->maxitems > resarr->nitems); Assert(resarr->capacity > 0); Assert(resarr->itemsarr != NULL); - Assert(resarr->nitems < resarr->capacity); + Assert(data != resarr->invalidval); + + idx = hash_any((void *) &data, sizeof(data)) & mask; + + while (true) + { + if (resarr->itemsarr[idx] == resarr->invalidval) + break; + idx = (idx + 1) & mask; + } - resarr->itemsarr[resarr->nitems] = data; + resarr->itemsarr[idx] = data; resarr->nitems++; } @@ -86,24 +130,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data) static bool ResourceArrayRemove(ResourceArray * resarr, Datum data) { - int i, - j, - lastidx; + uint32 i; + Datum idx; + Datum mask = resarr->capacity - 1; Assert(resarr->capacity > 0); Assert(resarr->itemsarr != NULL); + Assert(data != resarr->invalidval); - lastidx = ((int) resarr->nitems) - 1; - - for (i = lastidx; i >= 0; i--) + idx = hash_any((void *) &data, sizeof(data)) & mask; + for (i = 0; i < resarr->capacity; i++) { - if (resarr->itemsarr[i] == data) + if (resarr->itemsarr[idx] == data) { - for (j = i; j < lastidx; j++) - resarr->itemsarr[j] = resarr->itemsarr[j + 1]; + resarr->itemsarr[idx] = resarr->invalidval; resarr->nitems--; return true; } + idx = (idx + 1) & mask; } return false; @@ -119,27 +163,33 @@ static void ResourceArrayEnlarge(ResourceArray * resarr) { uint32 i, - oldcap, - oldnitems; + oldcap; Datum *olditemsarr; - if (resarr->nitems < resarr->capacity) + if (resarr->nitems < resarr->maxitems) return; /* nothing to do */ olditemsarr = resarr->itemsarr; oldcap = resarr->capacity; - oldnitems = resarr->nitems; resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE; resarr->itemsarr = (Datum *) MemoryContextAlloc(TopMemoryContext, resarr->capacity * sizeof(Datum)); + resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity); resarr->nitems = 0; + for (i = 0; i < resarr->capacity; i++) + resarr->itemsarr[i] = resarr->invalidval; + if (olditemsarr != NULL) { - for (i = 0; i < oldnitems; i++) - ResourceArrayAdd(resarr, olditemsarr[i]); + while (oldcap > 0) + { + oldcap--; + if (olditemsarr[oldcap] != resarr->invalidval) + ResourceArrayAdd(resarr, olditemsarr[oldcap]); + } pfree(olditemsarr); } } @@ -152,12 +202,24 @@ ResourceArrayEnlarge(ResourceArray * resarr) static bool ResourceArrayGetAny(ResourceArray * resarr, Datum *out) { + uint32 mask; + if (resarr->nitems == 0) return false; Assert(resarr->capacity > 0); + mask = resarr->capacity - 1; + + for (;;) + { + resarr->lastidx = resarr->lastidx & mask; + if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval) + break; + + resarr->lastidx++; + } - *out = resarr->itemsarr[resarr->nitems - 1]; + *out = resarr->itemsarr[resarr->lastidx]; return true; } @@ -170,6 +232,7 @@ ResourceArrayFree(ResourceArray * resarr) Assert(resarr->nitems == 0); resarr->capacity = 0; + resarr->maxitems = 0; if (!resarr->itemsarr) return; @@ -287,15 +350,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name) parent->firstchild = owner; } - ResourceArrayInit(&(owner->catrefarr)); - ResourceArrayInit(&(owner->catlistrefarr)); - ResourceArrayInit(&(owner->relrefarr)); - ResourceArrayInit(&(owner->planrefarr)); - ResourceArrayInit(&(owner->tupdescarr)); - ResourceArrayInit(&(owner->snapshotarr)); - ResourceArrayInit(&(owner->dsmarr)); - ResourceArrayInit(&(owner->bufferarr)); - ResourceArrayInit(&(owner->filearr)); + ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL)); + ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer)); + ResourceArrayInit(&(owner->filearr), (Datum) (-1)); return owner; }
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers