Commit d2158b02 authored by Teodor Sigaev's avatar Teodor Sigaev

* Add support NULL to GiST.

* some refactoring and simplify code int gistutil.c and gist.c
* now in some cases it can be called used-defined
  picksplit method for non-first column in index, but here
	is a place to do more.
* small fix of docs related to support NULL.
parent 86722057
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.11 2006/05/10 23:18:38 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.12 2006/05/24 11:01:39 teodor Exp $ -->
<chapter id="indexam"> <chapter id="indexam">
<title>Index Access Method Interface Definition</title> <title>Index Access Method Interface Definition</title>
...@@ -126,8 +126,7 @@ ...@@ -126,8 +126,7 @@
used to scan for rows with <literal>a = 4</literal>, which is wrong if the used to scan for rows with <literal>a = 4</literal>, which is wrong if the
index omits rows where <literal>b</> is null. index omits rows where <literal>b</> is null.
It is, however, OK to omit rows where the first indexed column is null. It is, however, OK to omit rows where the first indexed column is null.
(GiST currently does so.) Thus, Thus, <structfield>amindexnulls</structfield> should be set true only if the
<structfield>amindexnulls</structfield> should be set true only if the
index access method indexes all rows, including arbitrary combinations of index access method indexes all rows, including arbitrary combinations of
null values. null values.
</para> </para>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.56 2006/01/18 21:29:45 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.57 2006/05/24 11:01:39 teodor Exp $ -->
<chapter id="indexes"> <chapter id="indexes">
<title id="indexes-title">Indexes</title> <title id="indexes-title">Indexes</title>
...@@ -290,13 +290,13 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor); ...@@ -290,13 +290,13 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
</para> </para>
<para> <para>
A multicolumn GiST index can only be used when there is a query condition A multicolumn GiST index can be used with query conditions that
on its leading column. Conditions on additional columns restrict the involve any subset of the index's columns. Conditions on additional
entries returned by the index, but the condition on the first column is the columns restrict the entries returned by the index, but the condition on
most important one for determining how much of the index needs to be the first column is the most important one for determining how much of
scanned. A GiST index will be relatively ineffective if its first column the index needs to be scanned. A GiST index will be relatively
has only a few distinct values, even if there are many distinct values in ineffective if its first column has only a few distinct values, even if
additional columns. there are many distinct values in additional columns.
</para> </para>
<para> <para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.136 2006/05/19 16:15:17 teodor Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.137 2006/05/24 11:01:39 teodor Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -181,32 +181,13 @@ gistbuildCallback(Relation index, ...@@ -181,32 +181,13 @@ gistbuildCallback(Relation index,
{ {
GISTBuildState *buildstate = (GISTBuildState *) state; GISTBuildState *buildstate = (GISTBuildState *) state;
IndexTuple itup; IndexTuple itup;
GISTENTRY tmpcentry;
int i;
MemoryContext oldCtx; MemoryContext oldCtx;
/* GiST cannot index tuples with leading NULLs */
if (isnull[0])
return;
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx); oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
/* immediately compress keys to normalize */
for (i = 0; i < buildstate->numindexattrs; i++)
{
if (isnull[i])
values[i] = (Datum) 0;
else
{
gistcentryinit(&buildstate->giststate, i, &tmpcentry, values[i],
NULL, NULL, (OffsetNumber) 0,
-1 /* size is currently bogus */ , TRUE, FALSE);
values[i] = tmpcentry.key;
}
}
/* form an index tuple and point it at the heap tuple */ /* form an index tuple and point it at the heap tuple */
itup = index_form_tuple(buildstate->giststate.tupdesc, values, isnull); itup = gistFormTuple(&buildstate->giststate, index,
values, NULL /* size is currently bogus */, isnull);
itup->t_tid = htup->t_self; itup->t_tid = htup->t_self;
/* /*
...@@ -243,34 +224,16 @@ gistinsert(PG_FUNCTION_ARGS) ...@@ -243,34 +224,16 @@ gistinsert(PG_FUNCTION_ARGS)
#endif #endif
IndexTuple itup; IndexTuple itup;
GISTSTATE giststate; GISTSTATE giststate;
GISTENTRY tmpentry;
int i;
MemoryContext oldCtx; MemoryContext oldCtx;
MemoryContext insertCtx; MemoryContext insertCtx;
/* GiST cannot index tuples with leading NULLs */
if (isnull[0])
PG_RETURN_BOOL(false);
insertCtx = createTempGistContext(); insertCtx = createTempGistContext();
oldCtx = MemoryContextSwitchTo(insertCtx); oldCtx = MemoryContextSwitchTo(insertCtx);
initGISTstate(&giststate, r); initGISTstate(&giststate, r);
/* immediately compress keys to normalize */ itup = gistFormTuple(&giststate, r,
for (i = 0; i < r->rd_att->natts; i++) values, NULL /* size is currently bogus */, isnull);
{
if (isnull[i])
values[i] = (Datum) 0;
else
{
gistcentryinit(&giststate, i, &tmpentry, values[i],
NULL, NULL, (OffsetNumber) 0,
-1 /* size is currently bogus */ , TRUE, FALSE);
values[i] = tmpentry.key;
}
}
itup = index_form_tuple(giststate.tupdesc, values, isnull);
itup->t_tid = *ht_ctid; itup->t_tid = *ht_ctid;
gistdoinsert(r, itup, &giststate); gistdoinsert(r, itup, &giststate);
...@@ -937,51 +900,23 @@ gistmakedeal(GISTInsertState *state, GISTSTATE *giststate) ...@@ -937,51 +900,23 @@ gistmakedeal(GISTInsertState *state, GISTSTATE *giststate)
} }
/* /*
* gistSplit -- split a page in the tree. * simple split page
*/ */
SplitedPageLayout * static void
gistSplit(Relation r, gistSplitHalf(GIST_SPLITVEC *v, int len) {
Page page,
IndexTuple *itup, /* contains compressed entry */
int len,
GISTSTATE *giststate)
{
IndexTuple *lvectup,
*rvectup;
GIST_SPLITVEC v;
GistEntryVector *entryvec;
int i; int i;
OffsetNumber offInvTuples[ MaxOffsetNumber ];
int nOffInvTuples = 0;
SplitedPageLayout *res = NULL;
/* generate the item array */
entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY));
entryvec->n = len + 1;
for (i = 1; i <= len; i++)
{
Datum datum;
bool IsNull;
if (!GistPageIsLeaf(page) && GistTupleIsInvalid(itup[i - 1]))
/* remember position of invalid tuple */
offInvTuples[ nOffInvTuples++ ] = i;
if ( nOffInvTuples > 0 )
/* we can safely do not decompress other keys, because
we will do splecial processing, but
it's needed to find another invalid tuples */
continue;
datum = index_getattr(itup[i - 1], 1, giststate->tupdesc, &IsNull); v->spl_nright = v->spl_nleft = 0;
gistdentryinit(giststate, 0, &(entryvec->vector[i]), v->spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
datum, r, page, i, v->spl_right= (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
ATTSIZE(datum, giststate->tupdesc, 1, IsNull), for(i = 1; i <= len; i++)
FALSE, IsNull); if ( i<len/2 )
} v->spl_right[ v->spl_nright++ ] = i;
else
v->spl_left[ v->spl_nleft++ ] = i;
}
/* /*
* if it was invalid tuple then we need special processing. * if it was invalid tuple then we need special processing.
* We move all invalid tuples on right page. * We move all invalid tuples on right page.
* *
...@@ -991,37 +926,142 @@ gistSplit(Relation r, ...@@ -991,37 +926,142 @@ gistSplit(Relation r,
* Normally, we never exec this code, but after crash replay it's possible * Normally, we never exec this code, but after crash replay it's possible
* to get 'invalid' tuples (probability is low enough) * to get 'invalid' tuples (probability is low enough)
*/ */
if (nOffInvTuples > 0) static void
{ gistSplitByInvalid(GISTSTATE *giststate, GIST_SPLITVEC *v, IndexTuple *itup, int len) {
int i;
static OffsetNumber offInvTuples[ MaxOffsetNumber ];
int nOffInvTuples = 0;
for (i = 1; i <= len; i++)
if ( GistTupleIsInvalid(itup[i - 1]) )
offInvTuples[ nOffInvTuples++ ] = i;
if ( nOffInvTuples == len ) {
/* corner case, all tuples are invalid */
v->spl_rightvalid= v->spl_leftvalid = false;
gistSplitHalf( v, len );
} else {
GistSplitVec gsvp; GistSplitVec gsvp;
v.spl_right = offInvTuples; v->spl_right = offInvTuples;
v.spl_nright = nOffInvTuples; v->spl_nright = nOffInvTuples;
v.spl_rightvalid = false; v->spl_rightvalid = false;
v.spl_left = (OffsetNumber *) palloc(entryvec->n * sizeof(OffsetNumber)); v->spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
v.spl_nleft = 0; v->spl_nleft = 0;
for(i = 1; i <= len; i++) for(i = 1; i <= len; i++)
if ( !GistTupleIsInvalid(itup[i - 1]) ) if ( !GistTupleIsInvalid(itup[i - 1]) )
v.spl_left[ v.spl_nleft++ ] = i; v->spl_left[ v->spl_nleft++ ] = i;
v.spl_leftvalid = true; v->spl_leftvalid = true;
gsvp.idgrp = NULL; gsvp.idgrp = NULL;
gsvp.attrsize = v.spl_lattrsize; gsvp.attrsize = v->spl_lattrsize;
gsvp.attr = v.spl_lattr; gsvp.attr = v->spl_lattr;
gsvp.len = v.spl_nleft; gsvp.len = v->spl_nleft;
gsvp.entries = v.spl_left; gsvp.entries = v->spl_left;
gsvp.isnull = v.spl_lisnull; gsvp.isnull = v->spl_lisnull;
gistunionsubkeyvec(giststate, itup, &gsvp, 0);
}
}
/*
* trys to split page by attno key, in a case of null
* values move its to separate page.
*/
static void
gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate,
GIST_SPLITVEC *v, GistEntryVector *entryvec, int attno) {
int i;
static OffsetNumber offNullTuples[ MaxOffsetNumber ];
int nOffNullTuples = 0;
for (i = 1; i <= len; i++) {
Datum datum;
bool IsNull;
gistunionsubkeyvec(giststate, itup, &gsvp, true); if (!GistPageIsLeaf(page) && GistTupleIsInvalid(itup[i - 1])) {
gistSplitByInvalid(giststate, v, itup, len);
return;
} }
datum = index_getattr(itup[i - 1], attno+1, giststate->tupdesc, &IsNull);
gistdentryinit(giststate, attno, &(entryvec->vector[i]),
datum, r, page, i,
ATTSIZE(datum, giststate->tupdesc, attno+1, IsNull),
FALSE, IsNull);
if ( IsNull )
offNullTuples[ nOffNullTuples++ ] = i;
}
v->spl_leftvalid = v->spl_rightvalid = true;
if ( nOffNullTuples == len ) {
/*
* Corner case: All keys in attno column are null, we should try to
* by keys in next column. It all keys in all columns
* are NULL just split page half by half
*/
v->spl_risnull[attno] = v->spl_lisnull[attno] = TRUE;
if ( attno+1 == r->rd_att->natts )
gistSplitHalf( v, len );
else else
{ gistSplitByKey(r, page, itup, len, giststate, v, entryvec, attno+1);
/* there is no invalid tuples, so usial processing */ } else if ( nOffNullTuples > 0 ) {
gistUserPicksplit(r, entryvec, &v, itup, len, giststate); int j=0;
v.spl_leftvalid = v.spl_rightvalid = true;
/*
* We don't want to mix NULLs and not-NULLs keys
* on one page, so move nulls to right page
*/
v->spl_right = offNullTuples;
v->spl_nright = nOffNullTuples;
v->spl_risnull[attno] = TRUE;
v->spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
v->spl_nleft = 0;
for(i = 1; i <= len; i++)
if ( j<v->spl_nright && offNullTuples[j] == i )
j++;
else
v->spl_left[ v->spl_nleft++ ] = i;
v->spl_idgrp = NULL;
gistunionsubkey(giststate, itup, v, 0);
} else {
/*
* all keys are not-null
*/
gistUserPicksplit(r, entryvec, attno, v, itup, len, giststate);
} }
}
/*
* gistSplit -- split a page in the tree and fill struct
* used for XLOG and real writes buffers. Function is recursive, ie
* it will split page until keys will fit in every page.
*/
SplitedPageLayout *
gistSplit(Relation r,
Page page,
IndexTuple *itup, /* contains compressed entry */
int len,
GISTSTATE *giststate)
{
IndexTuple *lvectup,
*rvectup;
GIST_SPLITVEC v;
GistEntryVector *entryvec;
int i;
SplitedPageLayout *res = NULL;
/* generate the item array */
entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY));
entryvec->n = len + 1;
gistSplitByKey(r, page, itup, len, giststate,
&v, entryvec, 0);
/* form left and right vector */ /* form left and right vector */
lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1)); lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.56 2006/03/05 15:58:20 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.57 2006/05/24 11:01:39 teodor Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -361,7 +361,7 @@ gistindex_keytest(IndexTuple tuple, ...@@ -361,7 +361,7 @@ gistindex_keytest(IndexTuple tuple,
IncrIndexProcessed(); IncrIndexProcessed();
/* /*
* Tuple doesn't restore after crash recovery because of inclomplete * Tuple doesn't restore after crash recovery because of incomplete
* insert * insert
*/ */
if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple)) if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple))
...@@ -378,14 +378,15 @@ gistindex_keytest(IndexTuple tuple, ...@@ -378,14 +378,15 @@ gistindex_keytest(IndexTuple tuple,
key->sk_attno, key->sk_attno,
giststate->tupdesc, giststate->tupdesc,
&isNull); &isNull);
/* is the index entry NULL? */
if (isNull) if ( key->sk_flags & SK_ISNULL ) {
{ /* is the compared-to datum NULL? on non-leaf page it's possible
/* XXX eventually should check if SK_ISNULL */ to have nulls in childs :( */
if ( isNull || !GistPageIsLeaf(p) )
return true;
return false; return false;
} } else if ( isNull )
/* is the compared-to datum NULL? */
if (key->sk_flags & SK_ISNULL)
return false; return false;
gistdentryinit(giststate, key->sk_attno - 1, &de, gistdentryinit(giststate, key->sk_attno - 1, &de,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.13 2006/05/19 16:15:17 teodor Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.14 2006/05/24 11:01:39 teodor Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -26,30 +26,18 @@ ...@@ -26,30 +26,18 @@
#define RIGHT_ADDED 0x02 #define RIGHT_ADDED 0x02
#define BOTH_ADDED ( LEFT_ADDED | RIGHT_ADDED ) #define BOTH_ADDED ( LEFT_ADDED | RIGHT_ADDED )
static float gistpenalty(GISTSTATE *giststate, int attno,
GISTENTRY *key1, bool isNull1,
GISTENTRY *key2, bool isNull2);
/* /*
* This defines is only for shorter code, used in gistgetadjusted * static *S used for temrorary storage (saves stack and palloc() call)
* and gistadjsubkey only
*/ */
#define FILLITEM(evp, isnullkey, okey, okeyb, rkey, rkeyb) do { \
if (isnullkey) { \ static int attrsizeS[INDEX_MAX_KEYS];
gistentryinit((evp), rkey, r, NULL, \ static Datum attrS[INDEX_MAX_KEYS];
(OffsetNumber) 0, rkeyb, FALSE); \ static bool isnullS[INDEX_MAX_KEYS];
} else { \
gistentryinit((evp), okey, r, NULL, \
(OffsetNumber) 0, okeyb, FALSE); \
} \
} while(0)
#define FILLEV(isnull1, key1, key1b, isnull2, key2, key2b) do { \
FILLITEM(*ev0p, isnull1, key1, key1b, key2, key2b); \
FILLITEM(*ev1p, isnull2, key2, key2b, key1, key1b); \
} while(0);
static void gistpenalty(GISTSTATE *giststate, int attno,
GISTENTRY *key1, bool isNull1,
GISTENTRY *key2, bool isNull2, float *penalty);
/* /*
* Write itup vector to page, has no control of free space * Write itup vector to page, has no control of free space
...@@ -164,87 +152,115 @@ gistfillitupvec(IndexTuple *vec, int veclen, int *memlen) { ...@@ -164,87 +152,115 @@ gistfillitupvec(IndexTuple *vec, int veclen, int *memlen) {
} }
/* /*
* Return an IndexTuple containing the result of applying the "union" * Make unions of keys in IndexTuple vector, return FALSE if itvec contains
* method to the specified IndexTuple vector. * invalid tuple. Resulting Datums aren't compressed.
*/ */
IndexTuple
gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate) static bool
{ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, int startkey,
Datum attr[INDEX_MAX_KEYS]; Datum *attr, bool *isnull, int *attrsize ) {
bool isnull[INDEX_MAX_KEYS];
GistEntryVector *evec;
int i; int i;
GISTENTRY centry[INDEX_MAX_KEYS]; GistEntryVector *evec;
IndexTuple res;
evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ);
for (i = 0; i < len; i++) for (i = startkey; i < giststate->tupdesc->natts; i++) {
if (GistTupleIsInvalid(itvec[i]))
return gist_form_invalid_tuple(InvalidBlockNumber);
for (i = 0; i < r->rd_att->natts; i++)
{
Datum datum;
int j; int j;
int real_len;
real_len = 0; evec->n = 0;
for (j = 0; j < len; j++)
{ for (j = 0; j < len; j++) {
Datum datum;
bool IsNull; bool IsNull;
if (GistTupleIsInvalid(itvec[j]))
return FALSE; /* signals that union with invalid tuple => result is invalid */
datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull); datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull);
if (IsNull) if (IsNull)
continue; continue;
gistdentryinit(giststate, i, gistdentryinit(giststate, i,
&(evec->vector[real_len]), evec->vector + evec->n,
datum, datum,
NULL, NULL, (OffsetNumber) 0, NULL, NULL, (OffsetNumber) 0,
ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull), ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
FALSE, IsNull); FALSE, IsNull);
real_len++; evec->n++;
} }
/* If this tuple vector was all NULLs, the union is NULL */ /* If this tuple vector was all NULLs, the union is NULL */
if (real_len == 0) if ( evec->n == 0 ) {
{
attr[i] = (Datum) 0; attr[i] = (Datum) 0;
attrsize[i] = (Datum) 0;
isnull[i] = TRUE; isnull[i] = TRUE;
} } else {
else if (evec->n == 1) {
{
int datumsize;
if (real_len == 1)
{
evec->n = 2; evec->n = 2;
gistentryinit(evec->vector[1], evec->vector[1] = evec->vector[0];
evec->vector[0].key, r, NULL,
(OffsetNumber) 0, evec->vector[0].bytes, FALSE);
} }
else
evec->n = real_len;
/* Compress the result of the union and store in attr array */ /* Make union and store in attr array */
datum = FunctionCall2(&giststate->unionFn[i], attr[i] = FunctionCall2(&giststate->unionFn[i],
PointerGetDatum(evec), PointerGetDatum(evec),
PointerGetDatum(&datumsize)); PointerGetDatum(attrsize + i));
gistcentryinit(giststate, i, &centry[i], datum,
NULL, NULL, (OffsetNumber) 0,
datumsize, FALSE, FALSE);
isnull[i] = FALSE; isnull[i] = FALSE;
attr[i] = centry[i].key;
} }
} }
res = index_form_tuple(giststate->tupdesc, attr, isnull); return TRUE;
GistTupleSetValid(res);
return res;
} }
/*
* Return an IndexTuple containing the result of applying the "union"
* method to the specified IndexTuple vector.
*/
IndexTuple
gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
{
if ( !gistMakeUnionItVec(giststate, itvec, len, 0, attrS, isnullS, attrsizeS) )
return gist_form_invalid_tuple(InvalidBlockNumber);
return gistFormTuple(giststate, r, attrS, attrsizeS, isnullS);
}
/*
* makes union of two key
*/
static void
gistMakeUnionKey( GISTSTATE *giststate, int attno,
GISTENTRY *entry1, bool isnull1,
GISTENTRY *entry2, bool isnull2,
Datum *dst, int *dstsize, bool *dstisnull ) {
static char storage[ 2 * sizeof(GISTENTRY) + GEVHDRSZ ];
GistEntryVector *evec = (GistEntryVector*)storage;
evec->n = 2;
if ( isnull1 && isnull2 ) {
*dstisnull = TRUE;
*dst = (Datum)0;
*dstsize = 0;
} else {
if ( isnull1 == FALSE && isnull2 == FALSE ) {
evec->vector[0] = *entry1;
evec->vector[1] = *entry2;
} else if ( isnull1 == FALSE ) {
evec->vector[0] = *entry1;
evec->vector[1] = *entry1;
} else {
evec->vector[0] = *entry2;
evec->vector[1] = *entry2;
}
*dstisnull = FALSE;
*dst = FunctionCall2(&giststate->unionFn[attno],
PointerGetDatum(evec),
PointerGetDatum(dstsize));
}
}
/* /*
* Forms union of oldtup and addtup, if union == oldtup then return NULL * Forms union of oldtup and addtup, if union == oldtup then return NULL
...@@ -252,15 +268,9 @@ gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate) ...@@ -252,15 +268,9 @@ gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
IndexTuple IndexTuple
gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate) gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
{ {
GistEntryVector *evec; bool neednew = FALSE;
bool neednew = false; GISTENTRY oldentries[INDEX_MAX_KEYS],
bool isnull[INDEX_MAX_KEYS]; addentries[INDEX_MAX_KEYS];
Datum attr[INDEX_MAX_KEYS];
GISTENTRY centry[INDEX_MAX_KEYS],
oldatt[INDEX_MAX_KEYS],
addatt[INDEX_MAX_KEYS],
*ev0p,
*ev1p;
bool oldisnull[INDEX_MAX_KEYS], bool oldisnull[INDEX_MAX_KEYS],
addisnull[INDEX_MAX_KEYS]; addisnull[INDEX_MAX_KEYS];
IndexTuple newtup = NULL; IndexTuple newtup = NULL;
...@@ -269,147 +279,83 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis ...@@ -269,147 +279,83 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
if (GistTupleIsInvalid(oldtup) || GistTupleIsInvalid(addtup)) if (GistTupleIsInvalid(oldtup) || GistTupleIsInvalid(addtup))
return gist_form_invalid_tuple(ItemPointerGetBlockNumber(&(oldtup->t_tid))); return gist_form_invalid_tuple(ItemPointerGetBlockNumber(&(oldtup->t_tid)));
evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
evec->n = 2;
ev0p = &(evec->vector[0]);
ev1p = &(evec->vector[1]);
gistDeCompressAtt(giststate, r, oldtup, NULL, gistDeCompressAtt(giststate, r, oldtup, NULL,
(OffsetNumber) 0, oldatt, oldisnull); (OffsetNumber) 0, oldentries, oldisnull);
gistDeCompressAtt(giststate, r, addtup, NULL, gistDeCompressAtt(giststate, r, addtup, NULL,
(OffsetNumber) 0, addatt, addisnull); (OffsetNumber) 0, addentries, addisnull);
for (i = 0; i < r->rd_att->natts; i++) for(i = 0; i < r->rd_att->natts; i++) {
{ gistMakeUnionKey( giststate, i,
if (oldisnull[i] && addisnull[i]) oldentries + i, oldisnull[i],
{ addentries + i, addisnull[i],
attr[i] = (Datum) 0; attrS + i, attrsizeS + i, isnullS + i );
isnull[i] = TRUE;
}
else
{
Datum datum;
int datumsize;
FILLEV(oldisnull[i], oldatt[i].key, oldatt[i].bytes, if ( neednew )
addisnull[i], addatt[i].key, addatt[i].bytes); /* we already need new key, so we can skip check */
continue;
datum = FunctionCall2(&giststate->unionFn[i], if ( isnullS[i] )
PointerGetDatum(evec), /* union of key may be NULL if and only if both keys are NULL */
PointerGetDatum(&datumsize)); continue;
if (oldisnull[i] || addisnull[i]) if ( !addisnull[i] ) {
{ if ( oldisnull[i] )
if (oldisnull[i])
neednew = true; neednew = true;
} else {
else
{
bool result; bool result;
FunctionCall3(&giststate->equalFn[i], FunctionCall3(&giststate->equalFn[i],
ev0p->key, oldentries[i].key,
datum, attrS[i],
PointerGetDatum(&result)); PointerGetDatum(&result));
if (!result) if (!result)
neednew = true; neednew = true;
} }
gistcentryinit(giststate, i, &centry[i], datum,
NULL, NULL, (OffsetNumber) 0,
datumsize, FALSE, FALSE);
attr[i] = centry[i].key;
isnull[i] = FALSE;
} }
} }
if (neednew) if (neednew)
{ {
/* need to update key */ /* need to update key */
newtup = index_form_tuple(giststate->tupdesc, attr, isnull); newtup = gistFormTuple(giststate, r, attrS, attrsizeS, isnullS);
newtup->t_tid = oldtup->t_tid; newtup->t_tid = oldtup->t_tid;
} }
return newtup; return newtup;
} }
/*
* Forms unions of subkeys after page split, but
* uses only tuples aren't in groups of equalent tuples
*/
void void
gistunionsubkeyvec(GISTSTATE *giststate, IndexTuple *itvec, gistunionsubkeyvec(GISTSTATE *giststate, IndexTuple *itvec,
GistSplitVec *gsvp, bool isall) { GistSplitVec *gsvp, int startkey) {
int i; IndexTuple *cleanedItVec;
GistEntryVector *evec; int i, cleanedLen=0;
evec = palloc(((gsvp->len < 2) ? 2 : gsvp->len) * sizeof(GISTENTRY) + GEVHDRSZ);
for (i = (isall) ? 0 : 1; i < giststate->tupdesc->natts; i++) cleanedItVec = (IndexTuple*)palloc(sizeof(IndexTuple) * gsvp->len);
{
int j;
Datum datum;
int datumsize;
int real_len;
real_len = 0;
for (j = 0; j < gsvp->len; j++)
{
bool IsNull;
if ( gsvp->idgrp && gsvp->idgrp[gsvp->entries[j]]) for(i=0;i<gsvp->len;i++) {
if ( gsvp->idgrp && gsvp->idgrp[gsvp->entries[i]])
continue; continue;
datum = index_getattr(itvec[gsvp->entries[j] - 1], i + 1, cleanedItVec[cleanedLen++] = itvec[gsvp->entries[i] - 1];
giststate->tupdesc, &IsNull);
if (IsNull)
continue;
gistdentryinit(giststate, i,
&(evec->vector[real_len]),
datum,
NULL, NULL, (OffsetNumber) 0,
ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
FALSE, IsNull);
real_len++;
} }
if (real_len == 0) gistMakeUnionItVec(giststate, cleanedItVec, cleanedLen, startkey,
{ gsvp->attr, gsvp->isnull, gsvp->attrsize);
datum = (Datum) 0;
datumsize = 0;
gsvp->isnull[i] = true;
}
else
{
/*
* evec->vector[0].bytes may be not defined, so form union
* with itself
*/
if (real_len == 1)
{
evec->n = 2;
memcpy(&(evec->vector[1]), &(evec->vector[0]),
sizeof(GISTENTRY));
}
else
evec->n = real_len;
datum = FunctionCall2(&giststate->unionFn[i],
PointerGetDatum(evec),
PointerGetDatum(&datumsize));
gsvp->isnull[i] = false;
}
gsvp->attr[i] = datum; pfree( cleanedItVec );
gsvp->attrsize[i] = datumsize;
}
} }
/* /*
* unions subkey for after user picksplit over first column * unions subkeys for after user picksplit over attno-1 column
*/ */
static void void
gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl) gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl, int attno)
{ {
GistSplitVec gsvp; GistSplitVec gsvp;
...@@ -421,7 +367,7 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl) ...@@ -421,7 +367,7 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
gsvp.entries = spl->spl_left; gsvp.entries = spl->spl_left;
gsvp.isnull = spl->spl_lisnull; gsvp.isnull = spl->spl_lisnull;
gistunionsubkeyvec(giststate, itvec, &gsvp, false); gistunionsubkeyvec(giststate, itvec, &gsvp, attno);
gsvp.attrsize = spl->spl_rattrsize; gsvp.attrsize = spl->spl_rattrsize;
gsvp.attr = spl->spl_rattr; gsvp.attr = spl->spl_rattr;
...@@ -429,20 +375,20 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl) ...@@ -429,20 +375,20 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
gsvp.entries = spl->spl_right; gsvp.entries = spl->spl_right;
gsvp.isnull = spl->spl_risnull; gsvp.isnull = spl->spl_risnull;
gistunionsubkeyvec(giststate, itvec, &gsvp, false); gistunionsubkeyvec(giststate, itvec, &gsvp, attno);
} }
/* /*
* find group in vector with equal value * find group in vector with equal value
*/ */
int static int
gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl, int attno)
{ {
int i; int i;
int curid = 1; int curid = 1;
/* /*
* first key is always not null (see gistinsert), so we may not check for * attno key is always not null (see gistSplitByKey), so we may not check for
* nulls * nulls
*/ */
for (i = 0; i < spl->spl_nleft; i++) for (i = 0; i < spl->spl_nleft; i++)
...@@ -459,7 +405,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) ...@@ -459,7 +405,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
{ {
if (spl->spl_idgrp[spl->spl_right[j]]) if (spl->spl_idgrp[spl->spl_right[j]])
continue; continue;
FunctionCall3(&giststate->equalFn[0], FunctionCall3(&giststate->equalFn[attno],
valvec[spl->spl_left[i]].key, valvec[spl->spl_left[i]].key,
valvec[spl->spl_right[j]].key, valvec[spl->spl_right[j]].key,
PointerGetDatum(&result)); PointerGetDatum(&result));
...@@ -479,7 +425,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) ...@@ -479,7 +425,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
{ {
if (spl->spl_idgrp[spl->spl_left[j]]) if (spl->spl_idgrp[spl->spl_left[j]])
continue; continue;
FunctionCall3(&giststate->equalFn[0], FunctionCall3(&giststate->equalFn[attno],
valvec[spl->spl_left[i]].key, valvec[spl->spl_left[i]].key,
valvec[spl->spl_left[j]].key, valvec[spl->spl_left[j]].key,
PointerGetDatum(&result)); PointerGetDatum(&result));
...@@ -501,23 +447,20 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) ...@@ -501,23 +447,20 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
* Insert equivalent tuples to left or right page with minimum * Insert equivalent tuples to left or right page with minimum
* penalty * penalty
*/ */
void static void
gistadjsubkey(Relation r, gistadjsubkey(Relation r,
IndexTuple *itup, /* contains compressed entry */ IndexTuple *itup, /* contains compressed entry */
int len, int len,
GIST_SPLITVEC *v, GIST_SPLITVEC *v,
GISTSTATE *giststate) GISTSTATE *giststate,
int attno)
{ {
int curlen; int curlen;
OffsetNumber *curwpos; OffsetNumber *curwpos;
GISTENTRY entry, GISTENTRY entry,
identry[INDEX_MAX_KEYS], identry[INDEX_MAX_KEYS];
*ev0p, float lpenalty = 0,
*ev1p; rpenalty = 0;
float lpenalty,
rpenalty;
GistEntryVector *evec;
int datumsize;
bool isnull[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS];
int i, int i,
j; j;
...@@ -551,16 +494,9 @@ gistadjsubkey(Relation r, ...@@ -551,16 +494,9 @@ gistadjsubkey(Relation r,
} }
v->spl_nright = curlen; v->spl_nright = curlen;
evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
evec->n = 2;
ev0p = &(evec->vector[0]);
ev1p = &(evec->vector[1]);
/* add equivalent tuple */ /* add equivalent tuple */
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
Datum datum;
if (v->spl_idgrp[i + 1] == 0) /* already inserted */ if (v->spl_idgrp[i + 1] == 0) /* already inserted */
continue; continue;
gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0, gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0,
...@@ -577,17 +513,17 @@ gistadjsubkey(Relation r, ...@@ -577,17 +513,17 @@ gistadjsubkey(Relation r,
else else
{ {
/* where? */ /* where? */
for (j = 1; j < r->rd_att->natts; j++) for (j = attno+1; j < r->rd_att->natts; j++)
{ {
gistentryinit(entry, v->spl_lattr[j], r, NULL, gistentryinit(entry, v->spl_lattr[j], r, NULL,
(OffsetNumber) 0, v->spl_lattrsize[j], FALSE); (OffsetNumber) 0, v->spl_lattrsize[j], FALSE);
gistpenalty(giststate, j, &entry, v->spl_lisnull[j], lpenalty = gistpenalty(giststate, j, &entry, v->spl_lisnull[j],
&identry[j], isnull[j], &lpenalty); &identry[j], isnull[j]);
gistentryinit(entry, v->spl_rattr[j], r, NULL, gistentryinit(entry, v->spl_rattr[j], r, NULL,
(OffsetNumber) 0, v->spl_rattrsize[j], FALSE); (OffsetNumber) 0, v->spl_rattrsize[j], FALSE);
gistpenalty(giststate, j, &entry, v->spl_risnull[j], rpenalty = gistpenalty(giststate, j, &entry, v->spl_risnull[j],
&identry[j], isnull[j], &rpenalty); &identry[j], isnull[j]);
if (lpenalty != rpenalty) if (lpenalty != rpenalty)
break; break;
...@@ -600,55 +536,31 @@ gistadjsubkey(Relation r, ...@@ -600,55 +536,31 @@ gistadjsubkey(Relation r,
if (lpenalty < rpenalty) if (lpenalty < rpenalty)
{ {
v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED; v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED;
v->spl_left[v->spl_nleft] = i + 1; v->spl_left[v->spl_nleft++] = i + 1;
v->spl_nleft++;
for (j = 1; j < r->rd_att->natts; j++)
{
if (isnull[j] && v->spl_lisnull[j])
{
v->spl_lattr[j] = (Datum) 0;
v->spl_lattrsize[j] = 0;
}
else
{
FILLEV(v->spl_lisnull[j], v->spl_lattr[j], v->spl_lattrsize[j],
isnull[j], identry[j].key, identry[j].bytes);
datum = FunctionCall2(&giststate->unionFn[j], for (j = attno+1; j < r->rd_att->natts; j++)
PointerGetDatum(evec), {
PointerGetDatum(&datumsize)); gistentryinit(entry, v->spl_lattr[j], r, NULL,
(OffsetNumber) 0, v->spl_lattrsize[j], FALSE);
v->spl_lattr[j] = datum; gistMakeUnionKey( giststate, j,
v->spl_lattrsize[j] = datumsize; &entry, v->spl_lisnull[j],
v->spl_lisnull[j] = false; identry + j, isnull[j],
} v->spl_lattr + j, v->spl_lattrsize + j, v->spl_lisnull + j );
} }
} }
else else
{ {
v->spl_grpflag[v->spl_idgrp[i + 1]] |= RIGHT_ADDED; v->spl_grpflag[v->spl_idgrp[i + 1]] |= RIGHT_ADDED;
v->spl_right[v->spl_nright] = i + 1; v->spl_right[v->spl_nright++] = i + 1;
v->spl_nright++;
for (j = 1; j < r->rd_att->natts; j++)
{
if (isnull[j] && v->spl_risnull[j])
{
v->spl_rattr[j] = (Datum) 0;
v->spl_rattrsize[j] = 0;
}
else
{
FILLEV(v->spl_risnull[j], v->spl_rattr[j], v->spl_rattrsize[j],
isnull[j], identry[j].key, identry[j].bytes);
datum = FunctionCall2(&giststate->unionFn[j],
PointerGetDatum(evec),
PointerGetDatum(&datumsize));
v->spl_rattr[j] = datum; for (j = attno+1; j < r->rd_att->natts; j++)
v->spl_rattrsize[j] = datumsize; {
v->spl_risnull[j] = false; gistentryinit(entry, v->spl_rattr[j], r, NULL,
} (OffsetNumber) 0, v->spl_rattrsize[j], FALSE);
gistMakeUnionKey( giststate, j,
&entry, v->spl_risnull[j],
identry + j, isnull[j],
v->spl_rattr + j, v->spl_rattrsize + j, v->spl_risnull + j );
} }
} }
} }
...@@ -702,8 +614,8 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ ...@@ -702,8 +614,8 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
gistdentryinit(giststate, j, &entry, datum, r, p, i, gistdentryinit(giststate, j, &entry, datum, r, p, i,
ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull), ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull),
FALSE, IsNull); FALSE, IsNull);
gistpenalty(giststate, j, &entry, IsNull, usize = gistpenalty(giststate, j, &entry, IsNull,
&identry[j], isnull[j], &usize); &identry[j], isnull[j]);
if (which_grow[j] < 0 || usize < which_grow[j]) if (which_grow[j] < 0 || usize < which_grow[j])
{ {
...@@ -796,8 +708,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r, ...@@ -796,8 +708,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
else else
{ {
gistcentryinit(giststate, i, &centry[i], attdata[i], gistcentryinit(giststate, i, &centry[i], attdata[i],
NULL, NULL, (OffsetNumber) 0, r, NULL, (OffsetNumber) 0,
datumsize[i], FALSE, FALSE); (datumsize) ? datumsize[i] : -1,
(datumsize) ? FALSE : TRUE,
FALSE);
compatt[i] = centry[i].key; compatt[i] = centry[i].key;
} }
} }
...@@ -824,29 +738,35 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, ...@@ -824,29 +738,35 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
} }
} }
static void static float
gistpenalty(GISTSTATE *giststate, int attno, gistpenalty(GISTSTATE *giststate, int attno,
GISTENTRY *key1, bool isNull1, GISTENTRY *orig, bool isNullOrig,
GISTENTRY *key2, bool isNull2, float *penalty) GISTENTRY *add, bool isNullAdd)
{ {
if (giststate->penaltyFn[attno].fn_strict && (isNull1 || isNull2)) float penalty = 0.0;
*penalty = 0.0;
else if ( giststate->penaltyFn[attno].fn_strict==FALSE || ( isNullOrig == FALSE && isNullAdd == FALSE ) )
FunctionCall3(&giststate->penaltyFn[attno], FunctionCall3(&giststate->penaltyFn[attno],
PointerGetDatum(key1), PointerGetDatum(orig),
PointerGetDatum(key2), PointerGetDatum(add),
PointerGetDatum(penalty)); PointerGetDatum(&penalty));
else if ( isNullOrig && isNullAdd )
penalty = 0.0;
else
penalty = 1e10; /* try to prevent to mix null and non-null value */
return penalty;
} }
void void
gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v, gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GIST_SPLITVEC *v,
IndexTuple *itup, int len, GISTSTATE *giststate) IndexTuple *itup, int len, GISTSTATE *giststate)
{ {
/* /*
* now let the user-defined picksplit function set up the split vector; in * now let the user-defined picksplit function set up the split vector; in
* entryvec have no null value!! * entryvec have no null value!!
*/ */
FunctionCall2(&giststate->picksplitFn[0], FunctionCall2(&giststate->picksplitFn[attno],
PointerGetDatum(entryvec), PointerGetDatum(entryvec),
PointerGetDatum(v)); PointerGetDatum(v));
...@@ -856,16 +776,16 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v, ...@@ -856,16 +776,16 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
if (v->spl_right[v->spl_nright - 1] == InvalidOffsetNumber) if (v->spl_right[v->spl_nright - 1] == InvalidOffsetNumber)
v->spl_right[v->spl_nright - 1] = (OffsetNumber) (entryvec->n - 1); v->spl_right[v->spl_nright - 1] = (OffsetNumber) (entryvec->n - 1);
v->spl_lattr[0] = v->spl_ldatum; v->spl_lattr[attno] = v->spl_ldatum;
v->spl_rattr[0] = v->spl_rdatum; v->spl_rattr[attno] = v->spl_rdatum;
v->spl_lisnull[0] = false; v->spl_lisnull[attno] = false;
v->spl_risnull[0] = false; v->spl_risnull[attno] = false;
/* /*
* if index is multikey, then we must to try get smaller bounding box for * if index is multikey, then we must to try get smaller bounding box for
* subkey(s) * subkey(s)
*/ */
if (giststate->tupdesc->natts > 1) if (giststate->tupdesc->natts > 1 && attno+1 != giststate->tupdesc->natts)
{ {
int MaxGrpId; int MaxGrpId;
...@@ -873,17 +793,17 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v, ...@@ -873,17 +793,17 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
v->spl_grpflag = (char *) palloc0(sizeof(char) * entryvec->n); v->spl_grpflag = (char *) palloc0(sizeof(char) * entryvec->n);
v->spl_ngrp = (int *) palloc(sizeof(int) * entryvec->n); v->spl_ngrp = (int *) palloc(sizeof(int) * entryvec->n);
MaxGrpId = gistfindgroup(giststate, entryvec->vector, v); MaxGrpId = gistfindgroup(giststate, entryvec->vector, v, attno);
/* form union of sub keys for each page (l,p) */ /* form union of sub keys for each page (l,p) */
gistunionsubkey(giststate, itup, v); gistunionsubkey(giststate, itup, v, attno + 1);
/* /*
* if possible, we insert equivalent tuples with control by penalty * if possible, we insert equivalent tuples with control by penalty
* for a subkey(s) * for a subkey(s)
*/ */
if (MaxGrpId > 1) if (MaxGrpId > 1)
gistadjsubkey(r, itup, len, v, giststate); gistadjsubkey(r, itup, len, v, giststate, attno);
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.15 2006/05/19 16:15:17 teodor Exp $ * $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.16 2006/05/24 11:01:39 teodor Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -206,17 +206,6 @@ typedef struct ...@@ -206,17 +206,6 @@ typedef struct
/* root page of a gist index */ /* root page of a gist index */
#define GIST_ROOT_BLKNO 0 #define GIST_ROOT_BLKNO 0
/*
* When we update a relation on which we're doing a scan, we need to
* check the scan and fix it if the update affected any of the pages
* it touches. Otherwise, we can miss records that we should see.
* The only times we need to do this are for deletions and splits. See
* the code in gistscan.c for how the scan is fixed. These two
* constants tell us what sort of operation changed the index.
*/
#define GISTOP_DEL 0
/* #define GISTOP_SPLIT 1 */
#define ATTSIZE(datum, tupdesc, i, isnull) \ #define ATTSIZE(datum, tupdesc, i, isnull) \
( \ ( \
(isnull) ? 0 : \ (isnull) ? 0 : \
...@@ -291,12 +280,6 @@ extern IndexTuple gistgetadjusted(Relation r, ...@@ -291,12 +280,6 @@ extern IndexTuple gistgetadjusted(Relation r,
IndexTuple oldtup, IndexTuple oldtup,
IndexTuple addtup, IndexTuple addtup,
GISTSTATE *giststate); GISTSTATE *giststate);
extern int gistfindgroup(GISTSTATE *giststate,
GISTENTRY *valvec, GIST_SPLITVEC *spl);
extern void gistadjsubkey(Relation r,
IndexTuple *itup, int len,
GIST_SPLITVEC *v,
GISTSTATE *giststate);
extern IndexTuple gistFormTuple(GISTSTATE *giststate, extern IndexTuple gistFormTuple(GISTSTATE *giststate,
Relation r, Datum *attdata, int *datumsize, bool *isnull); Relation r, Datum *attdata, int *datumsize, bool *isnull);
...@@ -321,13 +304,15 @@ typedef struct { ...@@ -321,13 +304,15 @@ typedef struct {
} GistSplitVec; } GistSplitVec;
extern void gistunionsubkeyvec(GISTSTATE *giststate, extern void gistunionsubkeyvec(GISTSTATE *giststate,
IndexTuple *itvec, GistSplitVec *gsvp, bool isall); IndexTuple *itvec, GistSplitVec *gsvp, int startkey);
extern void gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec,
GIST_SPLITVEC *spl, int attno);
extern void GISTInitBuffer(Buffer b, uint32 f); extern void GISTInitBuffer(Buffer b, uint32 f);
extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, extern void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
Datum k, Relation r, Page pg, OffsetNumber o, Datum k, Relation r, Page pg, OffsetNumber o,
int b, bool l, bool isNull); int b, bool l, bool isNull);
void gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v, void gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GIST_SPLITVEC *v,
IndexTuple *itup, int len, GISTSTATE *giststate); IndexTuple *itup, int len, GISTSTATE *giststate);
/* gistvacuum.c */ /* gistvacuum.c */
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.333 2006/05/19 19:08:26 alvherre Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.334 2006/05/24 11:01:39 teodor Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200605191 #define CATALOG_VERSION_NO 200605241
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.42 2006/05/02 22:25:10 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.43 2006/05/24 11:01:39 teodor Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -114,7 +114,7 @@ DESCR("b-tree index access method"); ...@@ -114,7 +114,7 @@ DESCR("b-tree index access method");
DATA(insert OID = 405 ( hash 1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate )); DATA(insert OID = 405 ( hash 1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate ));
DESCR("hash index access method"); DESCR("hash index access method");
#define HASH_AM_OID 405 #define HASH_AM_OID 405
DATA(insert OID = 783 ( gist 100 7 0 f t f f t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate )); DATA(insert OID = 783 ( gist 100 7 0 f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate ));
DESCR("GiST index access method"); DESCR("GiST index access method");
#define GIST_AM_OID 783 #define GIST_AM_OID 783
DATA(insert OID = 2742 ( gin 100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate )); DATA(insert OID = 2742 ( gin 100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment