1996-08-26 22:02:12 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* gist.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* interface routines for the postgres GiST index access method.
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
2001-05-30 21:53:40 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2005-06-27 14:45:23 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.122 2005/06/27 12:45:21 teodor Exp $
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-10-20 10:32:11 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/genam.h"
|
2005-05-17 05:34:18 +02:00
|
|
|
#include "access/gist_private.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/gistscan.h"
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/index.h"
|
2004-02-10 04:42:45 +01:00
|
|
|
#include "commands/vacuum.h"
|
1999-09-18 21:08:25 +02:00
|
|
|
#include "miscadmin.h"
|
2005-05-17 02:59:30 +02:00
|
|
|
#include "utils/memutils.h"
|
1996-10-21 07:11:00 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
const XLogRecPtr XLogRecPtrForTemp = { 1, 1 };
|
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
/* Working state for gistbuild and its callback */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GISTSTATE giststate;
|
|
|
|
int numindexattrs;
|
|
|
|
double indtuples;
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContext tmpCtx;
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
} GISTBuildState;
|
|
|
|
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/* non-export function prototypes */
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
static void gistbuildCallback(Relation index,
|
2001-10-25 07:50:21 +02:00
|
|
|
HeapTuple htup,
|
2005-03-21 02:24:04 +01:00
|
|
|
Datum *values,
|
|
|
|
bool *isnull,
|
2001-10-25 07:50:21 +02:00
|
|
|
bool tupleIsAlive,
|
|
|
|
void *state);
|
2001-03-22 05:01:46 +01:00
|
|
|
static void gistdoinsert(Relation r,
|
|
|
|
IndexTuple itup,
|
|
|
|
GISTSTATE *GISTstate);
|
2005-06-14 13:45:14 +02:00
|
|
|
static void gistfindleaf(GISTInsertState *state,
|
2001-03-22 05:01:46 +01:00
|
|
|
GISTSTATE *giststate);
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
#define ROTATEDIST(d) do { \
|
2005-06-20 12:29:37 +02:00
|
|
|
SplitedPageLayout *tmp=(SplitedPageLayout*)palloc(sizeof(SplitedPageLayout)); \
|
|
|
|
memset(tmp,0,sizeof(SplitedPageLayout)); \
|
2005-06-14 13:45:14 +02:00
|
|
|
tmp->next = (d); \
|
|
|
|
(d)=tmp; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/*
|
2005-05-17 02:59:30 +02:00
|
|
|
* Create and return a temporary memory context for use by GiST. We
|
|
|
|
* _always_ invoke user-provided methods in a temporary memory
|
|
|
|
* context, so that memory leaks in those functions cannot cause
|
|
|
|
* problems. Also, we use some additional temporary contexts in the
|
|
|
|
* GiST code itself, to avoid the need to do some awkward manual
|
|
|
|
* memory management.
|
|
|
|
*/
|
|
|
|
MemoryContext
|
|
|
|
createTempGistContext(void)
|
|
|
|
{
|
|
|
|
return AllocSetContextCreate(CurrentMemoryContext,
|
|
|
|
"GiST temporary context",
|
|
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine to build an index. Basically calls insert over and over.
|
|
|
|
*
|
|
|
|
* XXX: it would be nice to implement some sort of bulk-loading
|
|
|
|
* algorithm, but it is not clear how to do that.
|
2001-05-07 02:43:27 +02:00
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistbuild(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
Relation heap = (Relation) PG_GETARG_POINTER(0);
|
|
|
|
Relation index = (Relation) PG_GETARG_POINTER(1);
|
|
|
|
IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
double reltuples;
|
|
|
|
GISTBuildState buildstate;
|
|
|
|
Buffer buffer;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We expect to be called exactly once for any index relation. If
|
|
|
|
* that's not the case, big trouble's what we have.
|
|
|
|
*/
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
if (RelationGetNumberOfBlocks(index) != 0)
|
2003-07-21 22:29:40 +02:00
|
|
|
elog(ERROR, "index \"%s\" already contains data",
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
RelationGetRelationName(index));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/* no locking is needed */
|
|
|
|
initGISTstate(&buildstate.giststate, index);
|
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
/* initialize the root page */
|
2005-06-27 14:45:23 +02:00
|
|
|
buffer = gistNewBuffer(index);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
GISTInitBuffer(buffer, F_LEAF);
|
2005-06-14 13:45:14 +02:00
|
|
|
if ( !index->rd_istemp ) {
|
|
|
|
XLogRecPtr recptr;
|
|
|
|
XLogRecData rdata;
|
|
|
|
Page page;
|
|
|
|
|
|
|
|
rdata.buffer = InvalidBuffer;
|
|
|
|
rdata.data = (char*)&(index->rd_node);
|
|
|
|
rdata.len = sizeof(RelFileNode);
|
|
|
|
rdata.next = NULL;
|
|
|
|
|
|
|
|
page = BufferGetPage(buffer);
|
|
|
|
|
|
|
|
START_CRIT_SECTION();
|
|
|
|
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_CREATE_INDEX, &rdata);
|
|
|
|
PageSetLSN(page, recptr);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
} else
|
|
|
|
PageSetLSN(BufferGetPage(buffer), XLogRecPtrForTemp);
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
WriteBuffer(buffer);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
/* build the index */
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
buildstate.numindexattrs = indexInfo->ii_NumIndexAttrs;
|
|
|
|
buildstate.indtuples = 0;
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
|
|
|
* create a temporary memory context that is reset once for each
|
|
|
|
* tuple inserted into the index
|
|
|
|
*/
|
2005-06-20 12:29:37 +02:00
|
|
|
buildstate.tmpCtx = createTempGistContext();
|
2000-07-15 00:18:02 +02:00
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
/* do the heap scan */
|
|
|
|
reltuples = IndexBuildHeapScan(heap, index, indexInfo,
|
2005-05-17 02:59:30 +02:00
|
|
|
gistbuildCallback, (void *) &buildstate);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* okay, all heap tuples are indexed */
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContextDelete(buildstate.tmpCtx);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-11 08:24:55 +02:00
|
|
|
/* since we just counted the # of tuples, may as well update stats */
|
|
|
|
IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples);
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
freeGISTstate(&buildstate.giststate);
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2000-06-14 07:24:50 +02:00
|
|
|
PG_RETURN_VOID();
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
/*
|
|
|
|
* Per-tuple callback from IndexBuildHeapScan
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
gistbuildCallback(Relation index,
|
|
|
|
HeapTuple htup,
|
2005-03-21 02:24:04 +01:00
|
|
|
Datum *values,
|
|
|
|
bool *isnull,
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
bool tupleIsAlive,
|
|
|
|
void *state)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
GISTBuildState *buildstate = (GISTBuildState *) state;
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
IndexTuple itup;
|
|
|
|
GISTENTRY tmpcentry;
|
|
|
|
int i;
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContext oldCtx;
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/* GiST cannot index tuples with leading NULLs */
|
2005-03-21 02:24:04 +01:00
|
|
|
if (isnull[0])
|
2001-10-25 07:50:21 +02:00
|
|
|
return;
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
2005-05-17 02:59:30 +02:00
|
|
|
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
/* immediately compress keys to normalize */
|
|
|
|
for (i = 0; i < buildstate->numindexattrs; i++)
|
|
|
|
{
|
2005-03-21 02:24:04 +01:00
|
|
|
if (isnull[i])
|
|
|
|
values[i] = (Datum) 0;
|
2001-10-25 07:50:21 +02:00
|
|
|
else
|
|
|
|
{
|
2005-03-21 02:24:04 +01:00
|
|
|
gistcentryinit(&buildstate->giststate, i, &tmpcentry, values[i],
|
2004-01-07 19:56:30 +01:00
|
|
|
NULL, NULL, (OffsetNumber) 0,
|
2005-05-17 02:59:30 +02:00
|
|
|
-1 /* size is currently bogus */, TRUE, FALSE);
|
2005-03-21 02:24:04 +01:00
|
|
|
values[i] = tmpcentry.key;
|
2001-08-10 16:34:28 +02:00
|
|
|
}
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* form an index tuple and point it at the heap tuple */
|
2005-03-21 02:24:04 +01:00
|
|
|
itup = index_form_tuple(buildstate->giststate.tupdesc, values, isnull);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
itup->t_tid = htup->t_self;
|
|
|
|
|
2001-08-10 16:34:28 +02:00
|
|
|
/*
|
|
|
|
* Since we already have the index relation locked, we call
|
2001-10-25 07:50:21 +02:00
|
|
|
* gistdoinsert directly. Normal access method calls dispatch through
|
|
|
|
* gistinsert, which locks the relation for write. This is the right
|
|
|
|
* thing to do if you're inserting single tups, but not when you're
|
|
|
|
* initializing the whole index at once.
|
2001-08-10 16:34:28 +02:00
|
|
|
*/
|
2005-03-21 02:24:04 +01:00
|
|
|
gistdoinsert(index, itup, &buildstate->giststate);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
|
2001-08-10 16:34:28 +02:00
|
|
|
buildstate->indtuples += 1;
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContextSwitchTo(oldCtx);
|
|
|
|
MemoryContextReset(buildstate->tmpCtx);
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
}
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* gistinsert -- wrapper for GiST tuple insertion.
|
1996-08-26 22:02:12 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This is the public interface routine for tuple insertion in GiSTs.
|
|
|
|
* It doesn't do any work; just locks the relation and passes the buck.
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
2000-06-13 09:35:40 +02:00
|
|
|
Datum
|
|
|
|
gistinsert(PG_FUNCTION_ARGS)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
Relation r = (Relation) PG_GETARG_POINTER(0);
|
2005-03-21 02:24:04 +01:00
|
|
|
Datum *values = (Datum *) PG_GETARG_POINTER(1);
|
|
|
|
bool *isnull = (bool *) PG_GETARG_POINTER(2);
|
2001-03-22 05:01:46 +01:00
|
|
|
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
|
2000-06-13 09:35:40 +02:00
|
|
|
#ifdef NOT_USED
|
2001-03-22 05:01:46 +01:00
|
|
|
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
2002-05-24 20:57:57 +02:00
|
|
|
bool checkUnique = PG_GETARG_BOOL(5);
|
2000-06-13 09:35:40 +02:00
|
|
|
#endif
|
1997-09-08 04:41:22 +02:00
|
|
|
IndexTuple itup;
|
|
|
|
GISTSTATE giststate;
|
|
|
|
GISTENTRY tmpentry;
|
|
|
|
int i;
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContext oldCtx;
|
|
|
|
MemoryContext insertCtx;
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/* GiST cannot index tuples with leading NULLs */
|
2005-03-21 02:24:04 +01:00
|
|
|
if (isnull[0])
|
|
|
|
PG_RETURN_BOOL(false);
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
insertCtx = createTempGistContext();
|
|
|
|
oldCtx = MemoryContextSwitchTo(insertCtx);
|
2005-05-17 02:59:30 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
initGISTstate(&giststate, r);
|
|
|
|
|
|
|
|
/* immediately compress keys to normalize */
|
|
|
|
for (i = 0; i < r->rd_att->natts; i++)
|
|
|
|
{
|
2005-03-21 02:24:04 +01:00
|
|
|
if (isnull[i])
|
|
|
|
values[i] = (Datum) 0;
|
2001-10-25 07:50:21 +02:00
|
|
|
else
|
|
|
|
{
|
2005-03-21 02:24:04 +01:00
|
|
|
gistcentryinit(&giststate, i, &tmpentry, values[i],
|
2004-01-07 19:56:30 +01:00
|
|
|
NULL, NULL, (OffsetNumber) 0,
|
2005-05-17 02:59:30 +02:00
|
|
|
-1 /* size is currently bogus */, TRUE, FALSE);
|
2005-03-21 02:24:04 +01:00
|
|
|
values[i] = tmpentry.key;
|
2001-08-10 16:34:28 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2005-03-21 02:24:04 +01:00
|
|
|
itup = index_form_tuple(giststate.tupdesc, values, isnull);
|
1997-09-07 07:04:48 +02:00
|
|
|
itup->t_tid = *ht_ctid;
|
|
|
|
|
2005-03-21 02:24:04 +01:00
|
|
|
gistdoinsert(r, itup, &giststate);
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/* cleanup */
|
2001-10-25 07:50:21 +02:00
|
|
|
freeGISTstate(&giststate);
|
2005-06-20 12:29:37 +02:00
|
|
|
MemoryContextSwitchTo(oldCtx);
|
|
|
|
MemoryContextDelete(insertCtx);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-03-21 02:24:04 +01:00
|
|
|
PG_RETURN_BOOL(true);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/*
|
|
|
|
* Workhouse routine for doing insertion into a GiST index. Note that
|
|
|
|
* this routine assumes it is invoked in a short-lived memory context,
|
|
|
|
* so it does not bother releasing palloc'd allocations.
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static void
|
2005-05-17 02:59:30 +02:00
|
|
|
gistdoinsert(Relation r, IndexTuple itup, GISTSTATE *giststate)
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2005-06-14 13:45:14 +02:00
|
|
|
GISTInsertState state;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
memset(&state, 0, sizeof(GISTInsertState));
|
2001-01-12 01:12:58 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
state.itup = (IndexTuple *) palloc(sizeof(IndexTuple));
|
|
|
|
state.itup[0] = (IndexTuple) palloc(IndexTupleSize(itup));
|
|
|
|
memcpy(state.itup[0], itup, IndexTupleSize(itup));
|
|
|
|
state.ituplen=1;
|
|
|
|
state.r = r;
|
|
|
|
state.key = itup->t_tid;
|
|
|
|
state.needInsertComplete = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
state.stack = (GISTInsertStack*)palloc0(sizeof(GISTInsertStack));
|
2005-06-14 13:45:14 +02:00
|
|
|
state.stack->blkno=GIST_ROOT_BLKNO;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
gistfindleaf(&state, giststate);
|
|
|
|
gistmakedeal(&state, giststate);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
static bool
|
|
|
|
gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) {
|
|
|
|
bool is_splitted = false;
|
2005-06-27 14:45:23 +02:00
|
|
|
bool is_leaf = (GistPageIsLeaf(state->stack->page)) ? true : false;
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if ( !is_leaf )
|
|
|
|
/*
|
|
|
|
* This node's key has been modified, either because a child
|
|
|
|
* split occurred or because we needed to adjust our key for
|
|
|
|
* an insert in a child node. Therefore, remove the old
|
|
|
|
* version of this node's key.
|
|
|
|
*/
|
|
|
|
|
|
|
|
PageIndexTupleDelete(state->stack->page, state->stack->childoffnum);
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
if (gistnospace(state->stack->page, state->itup, state->ituplen))
|
2001-03-22 05:01:46 +01:00
|
|
|
{
|
2005-06-14 13:45:14 +02:00
|
|
|
/* no space for insertion */
|
|
|
|
IndexTuple *itvec,
|
|
|
|
*newitup;
|
|
|
|
int tlen,olen;
|
2005-06-20 12:29:37 +02:00
|
|
|
SplitedPageLayout *dist=NULL, *ptr;
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
is_splitted = true;
|
|
|
|
itvec = gistextractbuffer(state->stack->buffer, &tlen);
|
|
|
|
olen=tlen;
|
|
|
|
itvec = gistjoinvector(itvec, &tlen, state->itup, state->ituplen);
|
|
|
|
newitup = gistSplit(state->r, state->stack->buffer, itvec, &tlen, &dist, giststate);
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
if ( !state->r->rd_istemp ) {
|
|
|
|
XLogRecPtr recptr;
|
|
|
|
XLogRecData *rdata;
|
|
|
|
|
|
|
|
rdata = formSplitRdata(state->r->rd_node, state->stack->blkno,
|
2005-06-27 14:45:23 +02:00
|
|
|
&(state->key), dist);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
START_CRIT_SECTION();
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
|
|
|
|
ptr = dist;
|
|
|
|
while(ptr) {
|
|
|
|
PageSetLSN(BufferGetPage(ptr->buffer), recptr);
|
|
|
|
PageSetTLI(BufferGetPage(ptr->buffer), ThisTimeLineID);
|
|
|
|
ptr=ptr->next;
|
2001-01-12 01:12:58 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
END_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
} else {
|
|
|
|
ptr = dist;
|
|
|
|
while(ptr) {
|
|
|
|
PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
|
|
|
|
ptr=ptr->next;
|
|
|
|
}
|
2005-06-14 13:45:14 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
state->itup = newitup;
|
|
|
|
state->ituplen = tlen; /* now tlen >= 2 */
|
2001-01-12 01:12:58 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
if ( state->stack->blkno == GIST_ROOT_BLKNO ) {
|
2005-06-27 14:45:23 +02:00
|
|
|
gistnewroot(state->r, state->stack->buffer, state->itup, state->ituplen, &(state->key));
|
2005-06-14 13:45:14 +02:00
|
|
|
state->needInsertComplete=false;
|
2005-06-27 14:45:23 +02:00
|
|
|
ptr = dist;
|
|
|
|
while(ptr) {
|
|
|
|
Page page = (Page)BufferGetPage(ptr->buffer);
|
|
|
|
GistPageGetOpaque(page)->rightlink = ( ptr->next ) ?
|
|
|
|
ptr->next->block.blkno : InvalidBlockNumber;
|
|
|
|
LockBuffer( ptr->buffer, GIST_UNLOCK );
|
|
|
|
WriteBuffer(ptr->buffer);
|
|
|
|
ptr=ptr->next;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Page page;
|
|
|
|
BlockNumber rightrightlink = InvalidBlockNumber;
|
|
|
|
SplitedPageLayout *ourpage=NULL;
|
|
|
|
GistNSN oldnsn;
|
|
|
|
GISTPageOpaque opaque;
|
|
|
|
|
|
|
|
/* move origpage to first in chain */
|
|
|
|
if ( dist->block.blkno != state->stack->blkno ) {
|
|
|
|
ptr = dist;
|
|
|
|
while(ptr->next) {
|
|
|
|
if ( ptr->next->block.blkno == state->stack->blkno ) {
|
|
|
|
ourpage = ptr->next;
|
|
|
|
ptr->next = ptr->next->next;
|
|
|
|
ourpage->next = dist;
|
|
|
|
dist = ourpage;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ptr=ptr->next;
|
|
|
|
}
|
|
|
|
Assert( ourpage != NULL );
|
|
|
|
} else
|
|
|
|
ourpage = dist;
|
|
|
|
|
|
|
|
|
|
|
|
/* now gets all needed data, and sets nsn's */
|
|
|
|
page = (Page)BufferGetPage(ourpage->buffer);
|
|
|
|
opaque = GistPageGetOpaque(page);
|
|
|
|
rightrightlink = opaque->rightlink;
|
|
|
|
oldnsn = opaque->nsn;
|
|
|
|
opaque->nsn = PageGetLSN(page);
|
|
|
|
opaque->rightlink = ourpage->next->block.blkno;
|
|
|
|
|
|
|
|
/* fills and write all new pages.
|
|
|
|
They isn't linked into tree yet */
|
|
|
|
|
|
|
|
ptr = ourpage->next;
|
|
|
|
while(ptr) {
|
|
|
|
page = (Page)BufferGetPage(ptr->buffer);
|
|
|
|
GistPageGetOpaque(page)->rightlink = ( ptr->next ) ?
|
|
|
|
ptr->next->block.blkno : rightrightlink;
|
|
|
|
/* only for last set oldnsn */
|
|
|
|
GistPageGetOpaque(page)->nsn = ( ptr->next ) ?
|
|
|
|
opaque->nsn : oldnsn;
|
|
|
|
|
|
|
|
LockBuffer(ptr->buffer, GIST_UNLOCK);
|
|
|
|
WriteBuffer(ptr->buffer);
|
|
|
|
ptr=ptr->next;
|
|
|
|
}
|
2005-06-14 13:45:14 +02:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
WriteNoReleaseBuffer( state->stack->buffer );
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-15 06:08:29 +02:00
|
|
|
/* enough space */
|
2005-06-27 14:45:23 +02:00
|
|
|
OffsetNumber l, off;
|
|
|
|
XLogRecPtr oldlsn;
|
2001-01-12 01:12:58 +01:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
off = ( PageIsEmpty(state->stack->page) ) ?
|
|
|
|
FirstOffsetNumber : OffsetNumberNext(PageGetMaxOffsetNumber(state->stack->page));
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
l = gistfillbuffer(state->r, state->stack->page, state->itup, state->ituplen, off);
|
2005-06-27 14:45:23 +02:00
|
|
|
oldlsn = PageGetLSN(state->stack->page);
|
2005-06-20 12:29:37 +02:00
|
|
|
if ( !state->r->rd_istemp ) {
|
|
|
|
OffsetNumber noffs=0, offs[ MAXALIGN( sizeof(OffsetNumber) ) / sizeof(OffsetNumber) ];
|
|
|
|
XLogRecPtr recptr;
|
|
|
|
XLogRecData *rdata;
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if ( !is_leaf ) {
|
|
|
|
/*only on inner page we should delete previous version */
|
2005-06-20 12:29:37 +02:00
|
|
|
offs[0] = state->stack->childoffnum;
|
|
|
|
noffs=1;
|
2005-06-14 13:45:14 +02:00
|
|
|
}
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
rdata = formUpdateRdata(state->r->rd_node, state->stack->blkno,
|
|
|
|
offs, noffs, false, state->itup, state->ituplen,
|
2005-06-27 14:45:23 +02:00
|
|
|
&(state->key));
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
START_CRIT_SECTION();
|
|
|
|
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_ENTRY_UPDATE, rdata);
|
|
|
|
PageSetLSN(state->stack->page, recptr);
|
|
|
|
PageSetTLI(state->stack->page, ThisTimeLineID);
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
} else
|
|
|
|
PageSetLSN(state->stack->page, XLogRecPtrForTemp);
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
if ( state->stack->blkno == GIST_ROOT_BLKNO )
|
|
|
|
state->needInsertComplete=false;
|
2005-06-27 14:45:23 +02:00
|
|
|
WriteNoReleaseBuffer(state->stack->buffer);
|
|
|
|
|
|
|
|
if (!is_leaf) /* small optimization: inform scan ablout deleting... */
|
|
|
|
gistadjscans(state->r, GISTOP_DEL, state->stack->blkno,
|
|
|
|
state->stack->childoffnum, PageGetLSN(state->stack->page), oldlsn );
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
if (state->ituplen > 1)
|
|
|
|
{ /* previous is_splitted==true */
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
|
|
|
* child was splited, so we must form union for insertion in
|
|
|
|
* parent
|
|
|
|
*/
|
2005-06-14 13:45:14 +02:00
|
|
|
IndexTuple newtup = gistunion(state->r, state->itup, state->ituplen, giststate);
|
2005-06-20 12:29:37 +02:00
|
|
|
ItemPointerSetBlockNumber(&(newtup->t_tid), state->stack->blkno);
|
2005-06-14 13:45:14 +02:00
|
|
|
state->itup[0] = newtup;
|
|
|
|
state->ituplen = 1;
|
2005-06-20 12:29:37 +02:00
|
|
|
} else if (is_leaf) {
|
|
|
|
/* itup[0] store key to adjust parent, we set it to valid
|
|
|
|
to correct check by GistTupleIsInvalid macro in gistgetadjusted() */
|
|
|
|
ItemPointerSetBlockNumber(&(state->itup[0]->t_tid), state->stack->blkno);
|
|
|
|
GistTupleSetValid( state->itup[0] );
|
2001-01-12 01:12:58 +01:00
|
|
|
}
|
|
|
|
}
|
2005-06-14 13:45:14 +02:00
|
|
|
return is_splitted;
|
2001-01-12 01:12:58 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/*
|
|
|
|
* returns stack of pages, all pages in stack are pinned, and
|
|
|
|
* leaf is X-locked
|
|
|
|
*/
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
static void
|
|
|
|
gistfindleaf(GISTInsertState *state, GISTSTATE *giststate)
|
2001-10-25 07:50:21 +02:00
|
|
|
{
|
2005-06-14 13:45:14 +02:00
|
|
|
ItemId iid;
|
2005-06-27 14:45:23 +02:00
|
|
|
IndexTuple idxtuple;
|
|
|
|
GISTPageOpaque opaque;
|
|
|
|
|
|
|
|
/* walk down, We don't lock page for a long time, but so
|
|
|
|
we should be ready to recheck path in a bad case...
|
|
|
|
We remember, that page->lsn should never be invalid. */
|
|
|
|
while( true ) {
|
|
|
|
|
|
|
|
if ( XLogRecPtrIsInvalid( state->stack->lsn ) )
|
|
|
|
state->stack->buffer = ReadBuffer(state->r, state->stack->blkno);
|
|
|
|
LockBuffer( state->stack->buffer, GIST_SHARE );
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
state->stack->page = (Page) BufferGetPage(state->stack->buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
opaque = GistPageGetOpaque(state->stack->page);
|
|
|
|
|
|
|
|
state->stack->lsn = PageGetLSN(state->stack->page);
|
|
|
|
Assert( state->r->rd_istemp || !XLogRecPtrIsInvalid( state->stack->lsn ) );
|
|
|
|
|
|
|
|
if ( state->stack->blkno != GIST_ROOT_BLKNO &&
|
|
|
|
XLByteLT( state->stack->parent->lsn, opaque->nsn) ) {
|
|
|
|
/* caused split non-root page is detected, go up to parent to choose best child */
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( state->stack->buffer );
|
|
|
|
state->stack = state->stack->parent;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
if (!GistPageIsLeaf(state->stack->page))
|
2001-10-25 07:50:21 +02:00
|
|
|
{
|
2005-06-14 13:45:14 +02:00
|
|
|
/*
|
|
|
|
* This is an internal page, so continue to walk down the
|
|
|
|
* tree. We find the child node that has the minimum insertion
|
|
|
|
* penalty and recursively invoke ourselves to modify that
|
|
|
|
* node. Once the recursive call returns, we may need to
|
|
|
|
* adjust the parent node for two reasons: the child node
|
|
|
|
* split, or the key in this node needs to be adjusted for the
|
|
|
|
* newly inserted key below us.
|
|
|
|
*/
|
2005-06-27 14:45:23 +02:00
|
|
|
GISTInsertStack *item=(GISTInsertStack*)palloc0(sizeof(GISTInsertStack));
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
state->stack->childoffnum = gistchoose(state->r, state->stack->page, state->itup[0], giststate);
|
|
|
|
|
|
|
|
iid = PageGetItemId(state->stack->page, state->stack->childoffnum);
|
2005-06-27 14:45:23 +02:00
|
|
|
idxtuple = (IndexTuple) PageGetItem(state->stack->page, iid);
|
|
|
|
item->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
item->parent = state->stack;
|
2005-06-27 14:45:23 +02:00
|
|
|
item->child = NULL;
|
|
|
|
if ( state->stack )
|
|
|
|
state->stack->child = item;
|
2005-06-14 13:45:14 +02:00
|
|
|
state->stack = item;
|
2005-06-27 14:45:23 +02:00
|
|
|
} else {
|
|
|
|
/* be carefull, during unlock/lock page may be changed... */
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
|
|
|
LockBuffer( state->stack->buffer, GIST_EXCLUSIVE );
|
|
|
|
state->stack->page = (Page) BufferGetPage(state->stack->buffer);
|
|
|
|
opaque = GistPageGetOpaque(state->stack->page);
|
|
|
|
|
|
|
|
if ( state->stack->blkno == GIST_ROOT_BLKNO ) {
|
|
|
|
/* the only page can become inner instead of leaf is a root page,
|
|
|
|
so for root we should recheck it */
|
|
|
|
if ( !GistPageIsLeaf(state->stack->page) ) {
|
|
|
|
/* very rarely situation: during unlock/lock index
|
|
|
|
with number of pages = 1 was increased */
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* we don't need to check root split, because checking
|
|
|
|
leaf/inner is enough to recognize split for root */
|
|
|
|
|
|
|
|
} else if ( XLByteLT( state->stack->parent->lsn, opaque->nsn) ) {
|
|
|
|
/* detecting split during unlock/lock, so we should
|
|
|
|
find better child on parent*/
|
|
|
|
|
|
|
|
/* forget buffer */
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( state->stack->buffer );
|
|
|
|
|
|
|
|
state->stack = state->stack->parent;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->stack->lsn = PageGetLSN( state->stack->page );
|
|
|
|
|
|
|
|
/* ok we found a leaf page and it X-locked */
|
2005-06-14 13:45:14 +02:00
|
|
|
break;
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
2001-05-31 20:16:55 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* now state->stack->(page, buffer and blkno) points to leaf page */
|
|
|
|
}
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/*
|
|
|
|
* Should have the same interface as XLogReadBuffer
|
|
|
|
*/
|
|
|
|
static Buffer
|
|
|
|
gistReadAndLockBuffer( bool unused, Relation r, BlockNumber blkno ) {
|
|
|
|
Buffer buffer = ReadBuffer( r, blkno );
|
|
|
|
LockBuffer( buffer, GIST_SHARE );
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse the tree to find path from root page,
|
|
|
|
* to prevent deadlocks, it should lock only one page simultaneously.
|
|
|
|
* Function uses in recovery and usial mode, so should work with different
|
|
|
|
* read functions (gistReadAndLockBuffer and XLogReadBuffer)
|
|
|
|
* returns from the begining of closest parent;
|
|
|
|
*/
|
|
|
|
GISTInsertStack*
|
|
|
|
gistFindPath( Relation r, BlockNumber child, Buffer (*myReadBuffer)(bool, Relation, BlockNumber) ) {
|
|
|
|
Page page;
|
|
|
|
Buffer buffer;
|
|
|
|
OffsetNumber i, maxoff;
|
|
|
|
ItemId iid;
|
|
|
|
IndexTuple idxtuple;
|
|
|
|
GISTInsertStack *top, *tail, *ptr;
|
|
|
|
BlockNumber blkno;
|
|
|
|
|
|
|
|
top = tail = (GISTInsertStack*)palloc0( sizeof(GISTInsertStack) );
|
|
|
|
top->blkno = GIST_ROOT_BLKNO;
|
|
|
|
|
|
|
|
while( top && top->blkno != child ) {
|
|
|
|
buffer = myReadBuffer(false, r, top->blkno); /* buffer locked */
|
|
|
|
page = (Page)BufferGetPage( buffer );
|
|
|
|
Assert( !GistPageIsLeaf(page) );
|
|
|
|
|
|
|
|
top->lsn = PageGetLSN(page);
|
|
|
|
|
|
|
|
if ( top->parent && XLByteLT( top->parent->lsn, GistPageGetOpaque(page)->nsn) &&
|
|
|
|
GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */) {
|
|
|
|
/* page splited while we thinking of... */
|
|
|
|
ptr = (GISTInsertStack*)palloc0( sizeof(GISTInsertStack) );
|
|
|
|
ptr->blkno = GistPageGetOpaque(page)->rightlink;
|
|
|
|
ptr->childoffnum = InvalidOffsetNumber;
|
|
|
|
ptr->parent = top;
|
|
|
|
ptr->next = NULL;
|
|
|
|
tail->next = ptr;
|
|
|
|
tail = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxoff = PageGetMaxOffsetNumber(page);
|
|
|
|
|
|
|
|
for(i = FirstOffsetNumber; i<= maxoff; i = OffsetNumberNext(i)) {
|
|
|
|
iid = PageGetItemId(page, i);
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid);
|
|
|
|
blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
|
|
|
|
if ( blkno == child ) {
|
|
|
|
OffsetNumber poff = InvalidOffsetNumber;
|
|
|
|
|
|
|
|
/* make childs links */
|
|
|
|
ptr = top;
|
|
|
|
while( ptr->parent ) {
|
|
|
|
/* set child link */
|
|
|
|
ptr->parent->child = ptr;
|
|
|
|
/* move childoffnum.. */
|
|
|
|
if ( ptr == top ) {
|
|
|
|
/*first iteration*/
|
|
|
|
poff = ptr->parent->childoffnum;
|
|
|
|
ptr->parent->childoffnum = ptr->childoffnum;
|
|
|
|
} else {
|
|
|
|
OffsetNumber tmp = ptr->parent->childoffnum;
|
|
|
|
ptr->parent->childoffnum = poff;
|
|
|
|
poff = tmp;
|
|
|
|
}
|
|
|
|
ptr = ptr->parent;
|
|
|
|
}
|
|
|
|
top->childoffnum = i;
|
|
|
|
LockBuffer( buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( buffer );
|
|
|
|
return top;
|
|
|
|
} else if ( GistPageGetOpaque(page)->level> 0 ) {
|
|
|
|
/* Install next inner page to the end of stack */
|
|
|
|
ptr = (GISTInsertStack*)palloc0( sizeof(GISTInsertStack) );
|
|
|
|
ptr->blkno = blkno;
|
|
|
|
ptr->childoffnum = i; /* set offsetnumber of child to child !!! */
|
|
|
|
ptr->parent = top;
|
|
|
|
ptr->next = NULL;
|
|
|
|
tail->next = ptr;
|
|
|
|
tail = ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LockBuffer( buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( buffer );
|
|
|
|
top = top->next;
|
2001-01-12 01:12:58 +01:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
|
|
|
|
return NULL;
|
2001-05-31 20:16:55 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/*
|
|
|
|
* Returns X-locked parent of stack page
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
gistFindCorrectParent( Relation r, GISTInsertStack *child ) {
|
|
|
|
GISTInsertStack *parent = child->parent;
|
|
|
|
|
|
|
|
LockBuffer( parent->buffer, GIST_EXCLUSIVE );
|
|
|
|
parent->page = (Page)BufferGetPage( parent->buffer );
|
|
|
|
|
|
|
|
|
|
|
|
/* here we don't need to distinguish between split and page update */
|
|
|
|
if ( parent->childoffnum == InvalidOffsetNumber || !XLByteEQ( parent->lsn, PageGetLSN(parent->page) ) ) {
|
|
|
|
/* parent is changed, look child in right links until found */
|
|
|
|
OffsetNumber i, maxoff;
|
|
|
|
ItemId iid;
|
|
|
|
IndexTuple idxtuple;
|
|
|
|
GISTInsertStack *ptr;
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
maxoff = PageGetMaxOffsetNumber(parent->page);
|
|
|
|
for(i = FirstOffsetNumber; i<= maxoff; i = OffsetNumberNext(i)) {
|
|
|
|
iid = PageGetItemId(parent->page, i);
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
|
|
|
|
if ( ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno ) {
|
|
|
|
/* yes!!, found */
|
|
|
|
parent->childoffnum = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parent->blkno = GistPageGetOpaque( parent->page )->rightlink;
|
|
|
|
LockBuffer( parent->buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( parent->buffer );
|
|
|
|
if ( parent->blkno == InvalidBlockNumber )
|
|
|
|
/* end of chain and still didn't found parent,
|
|
|
|
It's very-very rare situation when root splited */
|
|
|
|
break;
|
|
|
|
parent->buffer = ReadBuffer( r, parent->blkno );
|
|
|
|
LockBuffer( parent->buffer, GIST_EXCLUSIVE );
|
|
|
|
parent->page = (Page)BufferGetPage( parent->buffer );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* awful!!, we need search tree to find parent ... ,
|
|
|
|
but before we should release all old parent */
|
|
|
|
|
|
|
|
ptr = child->parent->parent; /* child->parent already released above */
|
|
|
|
while(ptr) {
|
|
|
|
ReleaseBuffer( ptr->buffer );
|
|
|
|
ptr = ptr->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ok, find new path */
|
|
|
|
ptr = parent = gistFindPath(r, child->blkno, gistReadAndLockBuffer);
|
|
|
|
Assert( ptr!=NULL );
|
|
|
|
|
|
|
|
/* read all buffers as supposed in caller */
|
|
|
|
while( ptr ) {
|
|
|
|
ptr->buffer = ReadBuffer( r, ptr->blkno );
|
|
|
|
ptr->page = (Page)BufferGetPage( ptr->buffer );
|
|
|
|
ptr = ptr->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* install new chain of parents to stack */
|
|
|
|
child->parent = parent;
|
|
|
|
parent->child = child;
|
|
|
|
|
|
|
|
/* make recursive call to normal processing */
|
|
|
|
gistFindCorrectParent( r, child );
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
void
|
|
|
|
gistmakedeal(GISTInsertState *state, GISTSTATE *giststate) {
|
|
|
|
int is_splitted;
|
|
|
|
ItemId iid;
|
|
|
|
IndexTuple oldtup, newtup;
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
/* walk up */
|
|
|
|
while( true ) {
|
|
|
|
/*
|
|
|
|
* After this call: 1. if child page was splited, then itup
|
|
|
|
* contains keys for each page 2. if child page wasn't splited,
|
|
|
|
* then itup contains additional for adjustment of current key
|
|
|
|
*/
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if ( state->stack->parent ) {
|
|
|
|
/* X-lock parent page before proceed child,
|
|
|
|
gistFindCorrectParent should find and lock it */
|
|
|
|
gistFindCorrectParent( state->r, state->stack );
|
|
|
|
}
|
2005-06-20 12:29:37 +02:00
|
|
|
is_splitted = gistplacetopage(state, giststate);
|
2001-01-12 01:12:58 +01:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* parent locked above, so release child buffer */
|
|
|
|
LockBuffer(state->stack->buffer, GIST_UNLOCK );
|
|
|
|
ReleaseBuffer( state->stack->buffer );
|
|
|
|
|
|
|
|
/* pop parent page from stack */
|
2005-06-14 13:45:14 +02:00
|
|
|
state->stack = state->stack->parent;
|
|
|
|
|
|
|
|
/* stack is void */
|
|
|
|
if ( ! state->stack )
|
|
|
|
break;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* child did not split, so we can check is it needed to update parent tuple */
|
2005-06-14 13:45:14 +02:00
|
|
|
if (!is_splitted)
|
2001-10-25 07:50:21 +02:00
|
|
|
{
|
2005-06-14 13:45:14 +02:00
|
|
|
/* parent's tuple */
|
|
|
|
iid = PageGetItemId(state->stack->page, state->stack->childoffnum);
|
|
|
|
oldtup = (IndexTuple) PageGetItem(state->stack->page, iid);
|
|
|
|
newtup = gistgetadjusted(state->r, oldtup, state->itup[0], giststate);
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
if (!newtup) { /* not need to update key */
|
|
|
|
LockBuffer( state->stack->buffer, GIST_UNLOCK );
|
2005-06-14 13:45:14 +02:00
|
|
|
break;
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
state->itup[0] = newtup;
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
2005-06-14 13:45:14 +02:00
|
|
|
} /* while */
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
/* release all parent buffers */
|
2005-06-14 13:45:14 +02:00
|
|
|
while( state->stack ) {
|
|
|
|
ReleaseBuffer(state->stack->buffer);
|
|
|
|
state->stack = state->stack->parent;
|
2005-05-17 02:59:30 +02:00
|
|
|
}
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
/* say to xlog that insert is completed */
|
2005-06-20 12:29:37 +02:00
|
|
|
if ( state->needInsertComplete && !state->r->rd_istemp )
|
|
|
|
gistxlogInsertCompletion(state->r->rd_node, &(state->key), 1);
|
|
|
|
}
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
static void
|
|
|
|
gistToRealOffset(OffsetNumber *arr, int len, OffsetNumber *reasloffset) {
|
|
|
|
int i;
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
for(i=0;i<len;i++)
|
|
|
|
arr[i] = reasloffset[ arr[i] ];
|
2001-08-10 16:34:28 +02:00
|
|
|
}
|
|
|
|
|
1996-08-26 22:02:12 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* gistSplit -- split a page in the tree.
|
1996-08-26 22:02:12 +02:00
|
|
|
*/
|
2005-06-20 12:29:37 +02:00
|
|
|
IndexTuple *
|
1996-08-26 22:02:12 +02:00
|
|
|
gistSplit(Relation r,
|
1997-09-07 07:04:48 +02:00
|
|
|
Buffer buffer,
|
2001-01-12 01:12:58 +01:00
|
|
|
IndexTuple *itup, /* contains compressed entry */
|
|
|
|
int *len,
|
2005-06-20 12:29:37 +02:00
|
|
|
SplitedPageLayout **dist,
|
2005-03-21 02:24:04 +01:00
|
|
|
GISTSTATE *giststate)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Page p;
|
2001-03-22 05:01:46 +01:00
|
|
|
Buffer leftbuf,
|
|
|
|
rightbuf;
|
|
|
|
Page left,
|
|
|
|
right;
|
|
|
|
IndexTuple *lvectup,
|
|
|
|
*rvectup,
|
|
|
|
*newtup;
|
|
|
|
BlockNumber lbknum,
|
|
|
|
rbknum;
|
1997-09-08 04:41:22 +02:00
|
|
|
GISTPageOpaque opaque;
|
|
|
|
GIST_SPLITVEC v;
|
2004-08-29 07:07:03 +02:00
|
|
|
GistEntryVector *entryvec;
|
2005-06-20 12:29:37 +02:00
|
|
|
int i, fakeoffset,
|
2001-03-22 05:01:46 +01:00
|
|
|
nlen;
|
2005-06-20 12:29:37 +02:00
|
|
|
OffsetNumber *realoffset;
|
|
|
|
IndexTuple *cleaneditup = itup;
|
|
|
|
int lencleaneditup = *len;
|
2005-06-27 14:45:23 +02:00
|
|
|
int level;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
p = (Page) BufferGetPage(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
opaque = GistPageGetOpaque(p);
|
|
|
|
level = opaque->level;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The root of the tree is the first block in the relation. If we're
|
|
|
|
* about to split the root, we need to do some hocus-pocus to enforce
|
|
|
|
* this guarantee.
|
|
|
|
*/
|
2005-05-17 02:59:30 +02:00
|
|
|
if (BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
leftbuf = gistNewBuffer(r);
|
2005-06-20 12:29:37 +02:00
|
|
|
GISTInitBuffer(leftbuf, opaque->flags&F_LEAF);
|
1997-09-07 07:04:48 +02:00
|
|
|
lbknum = BufferGetBlockNumber(leftbuf);
|
|
|
|
left = (Page) BufferGetPage(leftbuf);
|
2005-06-27 14:45:23 +02:00
|
|
|
GistPageGetOpaque(left)->level = level;
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
leftbuf = buffer;
|
2005-06-27 14:45:23 +02:00
|
|
|
/* IncrBufferRefCount(buffer); */
|
1997-09-07 07:04:48 +02:00
|
|
|
lbknum = BufferGetBlockNumber(buffer);
|
|
|
|
left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData));
|
|
|
|
}
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
rightbuf = gistNewBuffer(r);
|
2005-06-20 12:29:37 +02:00
|
|
|
GISTInitBuffer(rightbuf, opaque->flags&F_LEAF);
|
1997-09-07 07:04:48 +02:00
|
|
|
rbknum = BufferGetBlockNumber(rightbuf);
|
|
|
|
right = (Page) BufferGetPage(rightbuf);
|
2005-06-27 14:45:23 +02:00
|
|
|
GistPageGetOpaque(right)->level = level;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* generate the item array */
|
2005-06-20 12:29:37 +02:00
|
|
|
realoffset = palloc((*len + 1) * sizeof(OffsetNumber));
|
2004-03-30 17:45:33 +02:00
|
|
|
entryvec = palloc(GEVHDRSZ + (*len + 1) * sizeof(GISTENTRY));
|
|
|
|
entryvec->n = *len + 1;
|
2005-05-17 02:59:30 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
fakeoffset = FirstOffsetNumber;
|
2001-01-12 01:12:58 +01:00
|
|
|
for (i = 1; i <= *len; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-05-17 02:59:30 +02:00
|
|
|
Datum datum;
|
|
|
|
bool IsNull;
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
if (!GistPageIsLeaf(p) && GistTupleIsInvalid( itup[i - 1] )) {
|
|
|
|
entryvec->n--;
|
|
|
|
/* remember position of invalid tuple */
|
|
|
|
realoffset[ entryvec->n ] = i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2001-08-22 20:24:26 +02:00
|
|
|
datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull);
|
2005-06-20 12:29:37 +02:00
|
|
|
gistdentryinit(giststate, 0, &(entryvec->vector[fakeoffset]),
|
2001-10-25 07:50:21 +02:00
|
|
|
datum, r, p, i,
|
2005-05-17 02:59:30 +02:00
|
|
|
ATTSIZE(datum, giststate->tupdesc, 1, IsNull),
|
|
|
|
FALSE, IsNull);
|
2005-06-20 12:29:37 +02:00
|
|
|
realoffset[ fakeoffset ] = i;
|
|
|
|
fakeoffset++;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/*
|
|
|
|
* if it was invalid tuple then we need special processing. If
|
|
|
|
* it's possible, we move all invalid tuples on right page.
|
|
|
|
* We should remember, that union with invalid tuples
|
|
|
|
* is a invalid tuple.
|
|
|
|
*/
|
|
|
|
if ( entryvec->n != *len + 1 ) {
|
|
|
|
lencleaneditup = entryvec->n-1;
|
|
|
|
cleaneditup = (IndexTuple*)palloc(lencleaneditup * sizeof(IndexTuple));
|
|
|
|
for(i=1;i<entryvec->n;i++)
|
|
|
|
cleaneditup[i-1] = itup[ realoffset[ i ]-1 ];
|
|
|
|
|
|
|
|
if ( gistnospace( left, cleaneditup, lencleaneditup ) ) {
|
|
|
|
/* no space on left to put all good tuples, so picksplit */
|
|
|
|
gistUserPicksplit(r, entryvec, &v, cleaneditup, lencleaneditup, giststate);
|
|
|
|
v.spl_leftvalid = true;
|
|
|
|
v.spl_rightvalid = false;
|
|
|
|
gistToRealOffset( v.spl_left, v.spl_nleft, realoffset );
|
|
|
|
gistToRealOffset( v.spl_right, v.spl_nright, realoffset );
|
|
|
|
} else {
|
|
|
|
/* we can try to store all valid tuples on one page */
|
|
|
|
v.spl_right = (OffsetNumber*)palloc( entryvec->n * sizeof(OffsetNumber) );
|
|
|
|
v.spl_left = (OffsetNumber*)palloc( entryvec->n * sizeof(OffsetNumber) );
|
|
|
|
|
|
|
|
if ( lencleaneditup==0 ) {
|
|
|
|
/* all tuples are invalid, so moves half of its to right */
|
|
|
|
v.spl_leftvalid = v.spl_rightvalid = false;
|
|
|
|
v.spl_nright = 0;
|
|
|
|
v.spl_nleft = 0;
|
|
|
|
for(i=1;i<=*len;i++)
|
|
|
|
if ( i-1<*len/2 )
|
|
|
|
v.spl_left[ v.spl_nleft++ ] = i;
|
|
|
|
else
|
|
|
|
v.spl_right[ v.spl_nright++ ] = i;
|
|
|
|
} else {
|
|
|
|
/* we will not call gistUserPicksplit, just put good
|
|
|
|
tuples on left and invalid on right */
|
|
|
|
v.spl_nleft = lencleaneditup;
|
|
|
|
v.spl_nright = 0;
|
|
|
|
for(i=1;i<entryvec->n;i++)
|
|
|
|
v.spl_left[i-1] = i;
|
|
|
|
gistToRealOffset( v.spl_left, v.spl_nleft, realoffset );
|
|
|
|
v.spl_lattr[0] = v.spl_ldatum = (Datum)0;
|
|
|
|
v.spl_rattr[0] = v.spl_rdatum = (Datum)0;
|
|
|
|
v.spl_lisnull[0] = true;
|
|
|
|
v.spl_risnull[0] = true;
|
|
|
|
gistunionsubkey(r, giststate, itup, &v, true);
|
|
|
|
v.spl_leftvalid = true;
|
|
|
|
v.spl_rightvalid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* there is no invalid tuples, so usial processing */
|
|
|
|
gistUserPicksplit(r, entryvec, &v, itup, *len, giststate);
|
|
|
|
v.spl_leftvalid = v.spl_rightvalid = true;
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
/* form left and right vector */
|
2005-06-20 12:29:37 +02:00
|
|
|
lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (*len+1));
|
|
|
|
rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (*len+1));
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
for (i = 0; i < v.spl_nleft; i++)
|
|
|
|
lvectup[i] = itup[v.spl_left[i] - 1];
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
for (i = 0; i < v.spl_nright; i++)
|
|
|
|
rvectup[i] = itup[v.spl_right[i] - 1];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/* place invalid tuples on right page if itsn't done yet */
|
|
|
|
for (fakeoffset = entryvec->n; fakeoffset < *len+1 && lencleaneditup; fakeoffset++) {
|
|
|
|
rvectup[v.spl_nright++] = itup[realoffset[fakeoffset] - 1];
|
|
|
|
}
|
2001-08-10 16:34:28 +02:00
|
|
|
|
2005-05-17 02:59:30 +02:00
|
|
|
/* write on disk (may need another split) */
|
2001-03-22 05:01:46 +01:00
|
|
|
if (gistnospace(right, rvectup, v.spl_nright))
|
|
|
|
{
|
2001-01-12 01:12:58 +01:00
|
|
|
nlen = v.spl_nright;
|
2005-06-14 13:45:14 +02:00
|
|
|
newtup = gistSplit(r, rightbuf, rvectup, &nlen, dist, giststate);
|
2005-06-27 14:45:23 +02:00
|
|
|
/* ReleaseBuffer(rightbuf); */
|
2001-03-22 05:01:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-01-12 01:12:58 +01:00
|
|
|
OffsetNumber l;
|
2005-06-20 17:22:38 +02:00
|
|
|
char *ptr;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
l = gistfillbuffer(r, right, rvectup, v.spl_nright, FirstOffsetNumber);
|
|
|
|
/* XLOG stuff */
|
|
|
|
ROTATEDIST(*dist);
|
|
|
|
(*dist)->block.blkno = BufferGetBlockNumber(rightbuf);
|
|
|
|
(*dist)->block.num = v.spl_nright;
|
2005-06-20 17:22:38 +02:00
|
|
|
(*dist)->list = (IndexTupleData*)palloc( BLCKSZ );
|
|
|
|
ptr = (char*) ( (*dist)->list );
|
|
|
|
for(i=0;i<v.spl_nright;i++) {
|
|
|
|
memcpy( ptr, rvectup[i], IndexTupleSize( rvectup[i] ) );
|
|
|
|
ptr += IndexTupleSize( rvectup[i] );
|
|
|
|
}
|
|
|
|
(*dist)->lenlist = ptr - ( (char*) ( (*dist)->list ) );
|
2005-06-14 13:45:14 +02:00
|
|
|
(*dist)->buffer = rightbuf;
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
nlen = 1;
|
2001-03-22 05:01:46 +01:00
|
|
|
newtup = (IndexTuple *) palloc(sizeof(IndexTuple) * 1);
|
2005-06-20 12:29:37 +02:00
|
|
|
newtup[0] = ( v.spl_rightvalid ) ? gistFormTuple(giststate, r, v.spl_rattr, v.spl_rattrsize, v.spl_risnull)
|
|
|
|
: gist_form_invalid_tuple( rbknum );
|
|
|
|
ItemPointerSetBlockNumber(&(newtup[0]->t_tid), rbknum);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (gistnospace(left, lvectup, v.spl_nleft))
|
|
|
|
{
|
|
|
|
int llen = v.spl_nleft;
|
2001-01-12 01:12:58 +01:00
|
|
|
IndexTuple *lntup;
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
lntup = gistSplit(r, leftbuf, lvectup, &llen, dist, giststate);
|
2005-06-27 14:45:23 +02:00
|
|
|
/* ReleaseBuffer(leftbuf); */
|
2001-01-12 01:12:58 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
newtup = gistjoinvector(newtup, &nlen, lntup, llen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-01-12 01:12:58 +01:00
|
|
|
OffsetNumber l;
|
2005-06-20 17:22:38 +02:00
|
|
|
char *ptr;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
l = gistfillbuffer(r, left, lvectup, v.spl_nleft, FirstOffsetNumber);
|
|
|
|
/* XLOG stuff */
|
|
|
|
ROTATEDIST(*dist);
|
|
|
|
(*dist)->block.blkno = BufferGetBlockNumber(leftbuf);
|
|
|
|
(*dist)->block.num = v.spl_nleft;
|
2005-06-20 17:22:38 +02:00
|
|
|
(*dist)->list = (IndexTupleData*)palloc( BLCKSZ );
|
|
|
|
ptr = (char*) ( (*dist)->list );
|
|
|
|
for(i=0;i<v.spl_nleft;i++) {
|
|
|
|
memcpy( ptr, lvectup[i], IndexTupleSize( lvectup[i] ) );
|
|
|
|
ptr += IndexTupleSize( lvectup[i] );
|
|
|
|
}
|
|
|
|
(*dist)->lenlist = ptr - ( (char*) ( (*dist)->list ) );
|
2005-06-14 13:45:14 +02:00
|
|
|
(*dist)->buffer = leftbuf;
|
|
|
|
|
2005-06-20 17:22:38 +02:00
|
|
|
if (BufferGetBlockNumber(buffer) != GIST_ROOT_BLKNO)
|
|
|
|
PageRestoreTempPage(left, p);
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
nlen += 1;
|
2005-05-17 02:59:30 +02:00
|
|
|
newtup = (IndexTuple *) repalloc(newtup, sizeof(IndexTuple) * nlen);
|
2005-06-20 12:29:37 +02:00
|
|
|
newtup[nlen - 1] = ( v.spl_leftvalid ) ? gistFormTuple(giststate, r, v.spl_lattr, v.spl_lattrsize, v.spl_lisnull)
|
|
|
|
: gist_form_invalid_tuple( lbknum );
|
|
|
|
ItemPointerSetBlockNumber(&(newtup[nlen - 1]->t_tid), lbknum);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
GistClearTuplesDeleted(p);
|
|
|
|
|
2001-01-12 01:12:58 +01:00
|
|
|
*len = nlen;
|
|
|
|
return newtup;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-08-26 22:02:12 +02:00
|
|
|
|
2005-06-14 13:45:14 +02:00
|
|
|
void
|
2005-06-27 14:45:23 +02:00
|
|
|
gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer key)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Page page;
|
2005-06-27 14:45:23 +02:00
|
|
|
int level;
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
Assert( BufferGetBlockNumber(buffer) == GIST_ROOT_BLKNO );
|
2005-06-14 13:45:14 +02:00
|
|
|
page = BufferGetPage(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
level = GistPageGetOpaque(page)->level;
|
|
|
|
GISTInitBuffer(buffer, 0);
|
|
|
|
GistPageGetOpaque(page)->level = level+1;
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
gistfillbuffer(r, page, itup, len, FirstOffsetNumber);
|
2005-06-20 12:29:37 +02:00
|
|
|
if ( !r->rd_istemp ) {
|
2005-06-14 13:45:14 +02:00
|
|
|
XLogRecPtr recptr;
|
2005-06-20 12:29:37 +02:00
|
|
|
XLogRecData *rdata;
|
2005-06-14 13:45:14 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
rdata = formUpdateRdata(r->rd_node, GIST_ROOT_BLKNO,
|
2005-06-27 14:45:23 +02:00
|
|
|
NULL, 0, false, itup, len, key);
|
2005-06-14 13:45:14 +02:00
|
|
|
|
|
|
|
START_CRIT_SECTION();
|
|
|
|
|
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_NEW_ROOT, rdata);
|
|
|
|
PageSetLSN(page, recptr);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
} else
|
|
|
|
PageSetLSN(page, XLogRecPtrForTemp);
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
initGISTstate(GISTSTATE *giststate, Relation index)
|
1996-08-26 22:02:12 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
int i;
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
if (index->rd_att->natts > INDEX_MAX_KEYS)
|
2003-07-21 22:29:40 +02:00
|
|
|
elog(ERROR, "numberOfAttributes %d > %d",
|
2001-08-21 18:36:06 +02:00
|
|
|
index->rd_att->natts, INDEX_MAX_KEYS);
|
|
|
|
|
2001-08-22 20:24:26 +02:00
|
|
|
giststate->tupdesc = index->rd_att;
|
2001-05-31 20:16:55 +02:00
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
for (i = 0; i < index->rd_att->natts; i++)
|
|
|
|
{
|
2001-10-07 01:21:45 +02:00
|
|
|
fmgr_info_copy(&(giststate->consistentFn[i]),
|
2005-05-17 02:59:30 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->unionFn[i]),
|
2001-10-25 07:50:21 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_UNION_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->compressFn[i]),
|
2005-05-17 02:59:30 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->decompressFn[i]),
|
2005-05-17 02:59:30 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->penaltyFn[i]),
|
2001-10-25 07:50:21 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->picksplitFn[i]),
|
2005-05-17 02:59:30 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
|
|
|
fmgr_info_copy(&(giststate->equalFn[i]),
|
2001-10-25 07:50:21 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
|
2001-10-07 01:21:45 +02:00
|
|
|
CurrentMemoryContext);
|
2001-05-31 20:16:55 +02:00
|
|
|
}
|
2001-08-22 20:24:26 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-22 20:24:26 +02:00
|
|
|
void
|
2001-10-25 07:50:21 +02:00
|
|
|
freeGISTstate(GISTSTATE *giststate)
|
|
|
|
{
|
2001-08-22 20:24:26 +02:00
|
|
|
/* no work */
|
1996-08-26 22:02:12 +02:00
|
|
|
}
|
|
|
|
|