* Add support NULL to GiST.
authorTeodor Sigaev <teodor@sigaev.ru>
Wed, 24 May 2006 11:01:39 +0000 (11:01 +0000)
committerTeodor Sigaev <teodor@sigaev.ru>
Wed, 24 May 2006 11:01:39 +0000 (11:01 +0000)
* 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.

doc/src/sgml/indexam.sgml
doc/src/sgml/indices.sgml
src/backend/access/gist/gist.c
src/backend/access/gist/gistget.c
src/backend/access/gist/gistutil.c
src/include/access/gist_private.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h

index 61956cdfcf5d7386cef83c7b17c834d39ebf2be7..70fba4ecc0aadb737484450c34414a41f9f0c76c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
  <title>Index Access Method Interface Definition</title>
    used to scan for rows with <literal>a = 4</literal>, which is wrong if the
    index omits rows where <literal>b</> is null.
    It is, however, OK to omit rows where the first indexed column is null.
-   (GiST currently does so.)  Thus,
-   <structfield>amindexnulls</structfield> should be set true only if the
+   Thus, <structfield>amindexnulls</structfield> should be set true only if the
    index access method indexes all rows, including arbitrary combinations of
    null values.
   </para>
index 9bb19c2cee0b71cba4330a3e9277f0b8671f8c4e..10669c01557e5da6b7b2be24a0e3bbb25b58c475 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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">
  <title id="indexes-title">Indexes</title>
@@ -290,13 +290,13 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
   </para>
 
   <para>
-   A multicolumn GiST index can only be used when there is a query condition
-   on its leading column.  Conditions on additional columns restrict the
-   entries returned by the index, but the condition on the first column is the
-   most important one for determining how much of the index needs to be
-   scanned.  A GiST index will be relatively ineffective if its first column
-   has only a few distinct values, even if there are many distinct values in
-   additional columns.
+   A multicolumn GiST index can be used with query conditions that
+   involve any subset of the index's columns. Conditions on additional 
+   columns restrict the entries returned by the index, but the condition on 
+   the first column is the most important one for determining how much of 
+   the index needs to be scanned.  A GiST index will be relatively 
+   ineffective if its first column has only a few distinct values, even if 
+   there are many distinct values in additional columns.
   </para>
 
   <para>
index cb10cbc35bd5cfceb1b55cd861d95be4990051c9..54ac45ee2fea85dedd9df2a7ecacd800c2c04e2e 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,
 {
    GISTBuildState *buildstate = (GISTBuildState *) state;
    IndexTuple  itup;
-   GISTENTRY   tmpcentry;
-   int         i;
    MemoryContext oldCtx;
 
-   /* GiST cannot index tuples with leading NULLs */
-   if (isnull[0])
-       return;
-
    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 */
-   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;
 
    /*
@@ -243,34 +224,16 @@ gistinsert(PG_FUNCTION_ARGS)
 #endif
    IndexTuple  itup;
    GISTSTATE   giststate;
-   GISTENTRY   tmpentry;
-   int         i;
    MemoryContext oldCtx;
    MemoryContext insertCtx;
 
-   /* GiST cannot index tuples with leading NULLs */
-   if (isnull[0])
-       PG_RETURN_BOOL(false);
-
    insertCtx = createTempGistContext();
    oldCtx = MemoryContextSwitchTo(insertCtx);
 
    initGISTstate(&giststate, r);
 
-   /* immediately compress keys to normalize */
-   for (i = 0; i < r->rd_att->natts; i++)
-   {
-       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 = gistFormTuple(&giststate, r,
+       values, NULL /* size is currently bogus */, isnull);
    itup->t_tid = *ht_ctid;
 
    gistdoinsert(r, itup, &giststate);
@@ -937,7 +900,147 @@ gistmakedeal(GISTInsertState *state, GISTSTATE *giststate)
 }
 
 /*
- * gistSplit -- split a page in the tree.
+ * simple split page 
+ */
+static void
+gistSplitHalf(GIST_SPLITVEC *v, int len) {
+   int i;
+
+   v->spl_nright   = v->spl_nleft      = 0;
+   v->spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
+   v->spl_right= (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
+   for(i = 1; i <= len; i++)
+       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.
+ * We move all invalid tuples on right page. 
+ *
+ * if there is no place on left page, gistSplit will be called one more 
+ * time for left page.
+ *
+ * Normally, we never exec this code, but after crash replay it's possible
+ * to get 'invalid' tuples (probability is low enough)
+ */
+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;
+               
+       v->spl_right = offInvTuples;
+       v->spl_nright = nOffInvTuples;
+       v->spl_rightvalid = false;
+
+       v->spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
+       v->spl_nleft = 0;
+       for(i = 1; i <= len; i++) 
+           if ( !GistTupleIsInvalid(itup[i - 1]) )
+               v->spl_left[ v->spl_nleft++ ] = i;
+       v->spl_leftvalid = true;
+       
+       gsvp.idgrp = NULL;
+       gsvp.attrsize = v->spl_lattrsize;
+       gsvp.attr = v->spl_lattr;
+       gsvp.len = v->spl_nleft;
+       gsvp.entries = v->spl_left;
+       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;
+
+       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 
+           gistSplitByKey(r, page, itup, len, giststate, v, entryvec, attno+1);
+   } else if ( nOffNullTuples > 0 ) {
+       int j=0;
+       
+       /* 
+        * 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,
@@ -951,77 +1054,14 @@ gistSplit(Relation r,
    GIST_SPLITVEC v;
    GistEntryVector *entryvec;
    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);
-       gistdentryinit(giststate, 0, &(entryvec->vector[i]),
-                      datum, r, page, i,
-                      ATTSIZE(datum, giststate->tupdesc, 1, IsNull),
-                      FALSE, IsNull);
-   }
-
-   /*
-    * if it was invalid tuple then we need special processing.
-    * We move all invalid tuples on right page. 
-    *
-    * if there is no place on left page, gistSplit will be called one more 
-    * time for left page.
-    *
-    * Normally, we never exec this code, but after crash replay it's possible
-    * to get 'invalid' tuples (probability is low enough)
-    */
-   if (nOffInvTuples > 0)
-   {
-       GistSplitVec    gsvp;
-               
-       v.spl_right = offInvTuples;
-       v.spl_nright = nOffInvTuples;
-       v.spl_rightvalid = false;
-
-       v.spl_left = (OffsetNumber *) palloc(entryvec->n * sizeof(OffsetNumber));
-       v.spl_nleft = 0;
-       for(i = 1; i <= len; i++) 
-           if ( !GistTupleIsInvalid(itup[i - 1]) )
-               v.spl_left[ v.spl_nleft++ ] = i;
-       v.spl_leftvalid = true;
-       
-       gsvp.idgrp = NULL;
-       gsvp.attrsize = v.spl_lattrsize;
-       gsvp.attr = v.spl_lattr;
-       gsvp.len = v.spl_nleft;
-       gsvp.entries = v.spl_left;
-       gsvp.isnull = v.spl_lisnull;
-
-       gistunionsubkeyvec(giststate, itup, &gsvp, true);
-   }
-   else
-   {
-       /* there is no invalid tuples, so usial processing */
-       gistUserPicksplit(r, entryvec, &v, itup, len, giststate);
-       v.spl_leftvalid = v.spl_rightvalid = true;
-   }
-
+   gistSplitByKey(r, page, itup, len, giststate, 
+       &v, entryvec, 0);
 
    /* form left and right vector */
    lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
index 55d45b4a0383c29e6f089a3b8b2cb8b076ef5e82..dcb8f2da54b2443055ac9c59a557dfb959a0a37f 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,
    IncrIndexProcessed();
 
    /*
-    * Tuple doesn't restore after crash recovery because of inclomplete
+    * Tuple doesn't restore after crash recovery because of incomplete
     * insert
     */
    if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple))
@@ -378,14 +378,15 @@ gistindex_keytest(IndexTuple tuple,
                              key->sk_attno,
                              giststate->tupdesc,
                              &isNull);
-       /* is the index entry NULL? */
-       if (isNull)
-       {
-           /* XXX eventually should check if SK_ISNULL */
+
+       if ( key->sk_flags & SK_ISNULL ) {
+           /* is the compared-to datum NULL? on non-leaf page it's possible
+              to have nulls in childs :( */
+
+           if ( isNull || !GistPageIsLeaf(p) )
+               return true;
            return false;
-       }
-       /* is the compared-to datum NULL? */
-       if (key->sk_flags & SK_ISNULL)
+       } else if ( isNull )
            return false;
 
        gistdentryinit(giststate, key->sk_attno - 1, &de,
index 92798a27d300285ac5d7be31c7d7763e136f77a2..3db72aa199d8941d459573f32169390c70166fd6 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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"
 #define RIGHT_ADDED 0x02
 #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
- * and gistadjsubkey only
+ * static *S used for temrorary storage (saves stack and palloc() call)
  */
-#define FILLITEM(evp, isnullkey, okey, okeyb, rkey, rkeyb)  do { \
-   if (isnullkey) {                                              \
-       gistentryinit((evp), rkey, r, NULL,                       \
-                     (OffsetNumber) 0, rkeyb, FALSE);            \
-   } 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);
+
+static int         attrsizeS[INDEX_MAX_KEYS];
+static Datum   attrS[INDEX_MAX_KEYS];
+static bool        isnullS[INDEX_MAX_KEYS];
 
 /*
  * Write itup vector to page, has no control of free space
@@ -164,87 +152,115 @@ gistfillitupvec(IndexTuple *vec, int veclen, int *memlen) {
 }
 
 /*
- * Return an IndexTuple containing the result of applying the "union"
- * method to the specified IndexTuple vector.
+ * Make unions of keys in IndexTuple vector, return FALSE if itvec contains 
+ * invalid tuple. Resulting Datums aren't compressed.
  */
-IndexTuple
-gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
-{
-   Datum       attr[INDEX_MAX_KEYS];
-   bool        isnull[INDEX_MAX_KEYS];
-   GistEntryVector *evec;
+
+static bool 
+gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, int startkey, 
+                   Datum *attr, bool *isnull, int *attrsize ) {
    int         i;
-   GISTENTRY   centry[INDEX_MAX_KEYS];
-   IndexTuple  res;
+   GistEntryVector *evec;
 
    evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ);
 
-   for (i = 0; i < len; i++)
-       if (GistTupleIsInvalid(itvec[i]))
-           return gist_form_invalid_tuple(InvalidBlockNumber);
+   for (i = startkey; i < giststate->tupdesc->natts; i++) {
+       int     j;
 
-   for (i = 0; i < r->rd_att->natts; i++)
-   {
-       Datum       datum;
-       int         j;
-       int         real_len;
+       evec->n = 0;
 
-       real_len = 0;
-       for (j = 0; j < len; j++)
-       {
-           bool        IsNull;
+       for (j = 0; j < len; j++) {
+           Datum   datum;
+           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);
            if (IsNull)
                continue;
 
            gistdentryinit(giststate, i,
-                          &(evec->vector[real_len]),
+                          evec->vector + evec->n,
                           datum,
                           NULL, NULL, (OffsetNumber) 0,
                           ATTSIZE(datum, giststate->tupdesc, i + 1, IsNull),
                           FALSE, IsNull);
-           real_len++;
+           evec->n++;
        }
 
        /* If this tuple vector was all NULLs, the union is NULL */
-       if (real_len == 0)
-       {
+       if ( evec->n == 0 ) {
            attr[i] = (Datum) 0;
+           attrsize[i] = (Datum) 0;
            isnull[i] = TRUE;
-       }
-       else
-       {
-           int         datumsize;
-
-           if (real_len == 1)
-           {
+       } else {
+           if (evec->n == 1) {
                evec->n = 2;
-               gistentryinit(evec->vector[1],
-                             evec->vector[0].key, r, NULL,
-                             (OffsetNumber) 0, evec->vector[0].bytes, FALSE);
-           }
-           else
-               evec->n = real_len;
+               evec->vector[1] = evec->vector[0];
+           } 
 
-           /* Compress the result of the union and store in attr array */
-           datum = FunctionCall2(&giststate->unionFn[i],
+           /* Make union and store in attr array */
+           attr[i] = FunctionCall2(&giststate->unionFn[i],
                                  PointerGetDatum(evec),
-                                 PointerGetDatum(&datumsize));
+                                 PointerGetDatum(attrsize + i));
 
-           gistcentryinit(giststate, i, &centry[i], datum,
-                          NULL, NULL, (OffsetNumber) 0,
-                          datumsize, FALSE, FALSE);
            isnull[i] = FALSE;
-           attr[i] = centry[i].key;
        }
    }
 
-   res = index_form_tuple(giststate->tupdesc, attr, isnull);
-   GistTupleSetValid(res);
-   return res;
+   return TRUE;
+}
+
+/*
+ * 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
@@ -252,15 +268,9 @@ gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 IndexTuple
 gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 {
-   GistEntryVector *evec;
-   bool        neednew = false;
-   bool        isnull[INDEX_MAX_KEYS];
-   Datum       attr[INDEX_MAX_KEYS];
-   GISTENTRY   centry[INDEX_MAX_KEYS],
-               oldatt[INDEX_MAX_KEYS],
-               addatt[INDEX_MAX_KEYS],
-              *ev0p,
-              *ev1p;
+   bool        neednew = FALSE;
+   GISTENTRY   oldentries[INDEX_MAX_KEYS],
+               addentries[INDEX_MAX_KEYS];
    bool        oldisnull[INDEX_MAX_KEYS],
                addisnull[INDEX_MAX_KEYS];
    IndexTuple  newtup = NULL;
@@ -269,147 +279,83 @@ gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *gis
    if (GistTupleIsInvalid(oldtup) || GistTupleIsInvalid(addtup))
        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,
-                     (OffsetNumber) 0, oldatt, oldisnull);
+                     (OffsetNumber) 0, oldentries, oldisnull);
 
    gistDeCompressAtt(giststate, r, addtup, NULL,
-                     (OffsetNumber) 0, addatt, addisnull);
+                     (OffsetNumber) 0, addentries, addisnull);
 
-   for (i = 0; i < r->rd_att->natts; i++)
-   {
-       if (oldisnull[i] && addisnull[i])
-       {
-           attr[i] = (Datum) 0;
-           isnull[i] = TRUE;
-       }
-       else
-       {
-           Datum       datum;
-           int         datumsize;
+   for(i = 0; i < r->rd_att->natts; i++) {
+       gistMakeUnionKey( giststate, i,
+                           oldentries + i, oldisnull[i],
+                           addentries + i, addisnull[i],
+                           attrS + i, attrsizeS + i, isnullS + i );
 
-           FILLEV(oldisnull[i], oldatt[i].key, oldatt[i].bytes,
-                  addisnull[i], addatt[i].key, addatt[i].bytes);
+       if ( neednew )
+           /* we already need new key, so we can skip check */
+           continue;
 
-           datum = FunctionCall2(&giststate->unionFn[i],
-                                 PointerGetDatum(evec),
-                                 PointerGetDatum(&datumsize));
+       if ( isnullS[i] )
+           /* union of key may be NULL if and only if both keys are NULL */
+           continue;
 
-           if (oldisnull[i] || addisnull[i])
-           {
-               if (oldisnull[i])
-                   neednew = true;
-           }
-           else
-           {
-               bool        result;
+       if ( !addisnull[i] ) {
+           if ( oldisnull[i] )
+               neednew = true;
+           else {
+               bool        result;
 
                FunctionCall3(&giststate->equalFn[i],
-                             ev0p->key,
-                             datum,
-                             PointerGetDatum(&result));
+                               oldentries[i].key,
+                               attrS[i],
+                               PointerGetDatum(&result));
 
                if (!result)
                    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)
    {
        /* 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;
    }
 
    return newtup;
 }
 
+/*
+ * Forms unions of subkeys after page split, but
+ * uses only tuples aren't in groups of equalent tuples
+ */
 void 
 gistunionsubkeyvec(GISTSTATE *giststate,  IndexTuple *itvec, 
-                           GistSplitVec *gsvp, bool isall) {
-   int         i;
-   GistEntryVector *evec;
-
-   evec = palloc(((gsvp->len < 2) ? 2 : gsvp->len) * sizeof(GISTENTRY) + GEVHDRSZ);
+                           GistSplitVec *gsvp, int startkey) {
+   IndexTuple  *cleanedItVec;
+   int         i, cleanedLen=0;
 
-   for (i = (isall) ? 0 : 1; i < giststate->tupdesc->natts; i++)
-   {
-       int         j;
-       Datum       datum;
-       int         datumsize;
-       int         real_len;
+   cleanedItVec = (IndexTuple*)palloc(sizeof(IndexTuple) * gsvp->len);
 
-       real_len = 0;
-       for (j = 0; j < gsvp->len; j++)
-       {
-           bool        IsNull;
-
-           if ( gsvp->idgrp && gsvp->idgrp[gsvp->entries[j]])
-               continue;
-
-           datum = index_getattr(itvec[gsvp->entries[j] - 1], 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++;
+   for(i=0;i<gsvp->len;i++) {
+       if ( gsvp->idgrp && gsvp->idgrp[gsvp->entries[i]])
+           continue;
 
-       }
+       cleanedItVec[cleanedLen++] = itvec[gsvp->entries[i] - 1];
+   }
 
-       if (real_len == 0)
-       {
-           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;
-       }
+    gistMakeUnionItVec(giststate, cleanedItVec, cleanedLen, startkey, 
+       gsvp->attr, gsvp->isnull, gsvp->attrsize);
 
-       gsvp->attr[i] = datum;
-       gsvp->attrsize[i] = datumsize;
-   }
+   pfree( cleanedItVec );
 }
 
 /*
- * unions subkey for after user picksplit over first column
+ * unions subkeys for after user picksplit over attno-1 column
  */
-static void
-gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
+void
+gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl, int attno)
 {
    GistSplitVec    gsvp;
 
@@ -421,7 +367,7 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
    gsvp.entries = spl->spl_left;
    gsvp.isnull = spl->spl_lisnull;
 
-   gistunionsubkeyvec(giststate, itvec, &gsvp, false);
+   gistunionsubkeyvec(giststate, itvec, &gsvp, attno);
 
    gsvp.attrsize = spl->spl_rattrsize;
    gsvp.attr = spl->spl_rattr;
@@ -429,20 +375,20 @@ gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl)
    gsvp.entries = spl->spl_right;
    gsvp.isnull = spl->spl_risnull;
 
-   gistunionsubkeyvec(giststate, itvec, &gsvp, false);
+   gistunionsubkeyvec(giststate, itvec, &gsvp, attno);
 }
 
 /*
  * find group in vector with equal value
  */
-int
-gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
+static int
+gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl, int attno)
 {
    int         i;
    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
     */
    for (i = 0; i < spl->spl_nleft; i++)
@@ -459,7 +405,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
        {
            if (spl->spl_idgrp[spl->spl_right[j]])
                continue;
-           FunctionCall3(&giststate->equalFn[0],
+           FunctionCall3(&giststate->equalFn[attno],
                          valvec[spl->spl_left[i]].key,
                          valvec[spl->spl_right[j]].key,
                          PointerGetDatum(&result));
@@ -479,7 +425,7 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
            {
                if (spl->spl_idgrp[spl->spl_left[j]])
                    continue;
-               FunctionCall3(&giststate->equalFn[0],
+               FunctionCall3(&giststate->equalFn[attno],
                              valvec[spl->spl_left[i]].key,
                              valvec[spl->spl_left[j]].key,
                              PointerGetDatum(&result));
@@ -501,23 +447,20 @@ gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl)
  * Insert equivalent tuples to left or right page with minimum
  * penalty
  */
-void
+static void
 gistadjsubkey(Relation r,
              IndexTuple *itup, /* contains compressed entry */
              int len,
              GIST_SPLITVEC *v,
-             GISTSTATE *giststate)
+             GISTSTATE *giststate,
+             int attno)
 {
    int         curlen;
    OffsetNumber *curwpos;
    GISTENTRY   entry,
-               identry[INDEX_MAX_KEYS],
-              *ev0p,
-              *ev1p;
-   float       lpenalty,
-               rpenalty;
-   GistEntryVector *evec;
-   int         datumsize;
+               identry[INDEX_MAX_KEYS];
+   float       lpenalty = 0,
+               rpenalty = 0;
    bool        isnull[INDEX_MAX_KEYS];
    int         i,
                j;
@@ -551,16 +494,9 @@ gistadjsubkey(Relation r,
    }
    v->spl_nright = curlen;
 
-   evec = palloc(2 * sizeof(GISTENTRY) + GEVHDRSZ);
-   evec->n = 2;
-   ev0p = &(evec->vector[0]);
-   ev1p = &(evec->vector[1]);
-
    /* add equivalent tuple */
    for (i = 0; i < len; i++)
    {
-       Datum       datum;
-
        if (v->spl_idgrp[i + 1] == 0)   /* already inserted */
            continue;
        gistDeCompressAtt(giststate, r, itup[i], NULL, (OffsetNumber) 0,
@@ -577,17 +513,17 @@ gistadjsubkey(Relation r,
        else
        {
            /* 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,
                              (OffsetNumber) 0, v->spl_lattrsize[j], FALSE);
-               gistpenalty(giststate, j, &entry, v->spl_lisnull[j],
-                           &identry[j], isnull[j], &lpenalty);
+               lpenalty = gistpenalty(giststate, j, &entry, v->spl_lisnull[j],
+                                       &identry[j], isnull[j]);
 
                gistentryinit(entry, v->spl_rattr[j], r, NULL,
                              (OffsetNumber) 0, v->spl_rattrsize[j], FALSE);
-               gistpenalty(giststate, j, &entry, v->spl_risnull[j],
-                           &identry[j], isnull[j], &rpenalty);
+               rpenalty = gistpenalty(giststate, j, &entry, v->spl_risnull[j],
+                                       &identry[j], isnull[j]);
 
                if (lpenalty != rpenalty)
                    break;
@@ -600,55 +536,31 @@ gistadjsubkey(Relation r,
        if (lpenalty < rpenalty)
        {
            v->spl_grpflag[v->spl_idgrp[i + 1]] |= LEFT_ADDED;
-           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);
+           v->spl_left[v->spl_nleft++] = i + 1;
 
-                   datum = FunctionCall2(&giststate->unionFn[j],
-                                         PointerGetDatum(evec),
-                                         PointerGetDatum(&datumsize));
-
-                   v->spl_lattr[j] = datum;
-                   v->spl_lattrsize[j] = datumsize;
-                   v->spl_lisnull[j] = false;
-               }
+           for (j = attno+1; j < r->rd_att->natts; j++)
+           {
+               gistentryinit(entry, v->spl_lattr[j], r, NULL,
+                             (OffsetNumber) 0, v->spl_lattrsize[j], FALSE);
+               gistMakeUnionKey( giststate, j,
+                           &entry, v->spl_lisnull[j],
+                           identry + j, isnull[j],
+                           v->spl_lattr + j, v->spl_lattrsize + j, v->spl_lisnull + j );
            }
        }
        else
        {
            v->spl_grpflag[v->spl_idgrp[i + 1]] |= RIGHT_ADDED;
-           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);
+           v->spl_right[v->spl_nright++] = i + 1;
 
-                   datum = FunctionCall2(&giststate->unionFn[j],
-                                         PointerGetDatum(evec),
-                                         PointerGetDatum(&datumsize));
-
-                   v->spl_rattr[j] = datum;
-                   v->spl_rattrsize[j] = datumsize;
-                   v->spl_risnull[j] = false;
-               }
+           for (j = attno+1; j < r->rd_att->natts; j++)
+           {
+               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 */
            gistdentryinit(giststate, j, &entry, datum, r, p, i,
                           ATTSIZE(datum, giststate->tupdesc, j + 1, IsNull),
                           FALSE, IsNull);
-           gistpenalty(giststate, j, &entry, IsNull,
-                       &identry[j], isnull[j], &usize);
+           usize = gistpenalty(giststate, j, &entry, IsNull,
+                               &identry[j], isnull[j]);
 
            if (which_grow[j] < 0 || usize < which_grow[j])
            {
@@ -796,8 +708,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
        else
        {
            gistcentryinit(giststate, i, &centry[i], attdata[i],
-                          NULL, NULL, (OffsetNumber) 0,
-                          datumsize[i], FALSE, FALSE);
+                          r, NULL, (OffsetNumber) 0,
+                          (datumsize) ? datumsize[i] : -1, 
+                          (datumsize) ? FALSE : TRUE, 
+                          FALSE);
            compatt[i] = centry[i].key;
        }
    }
@@ -824,29 +738,35 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
    }
 }
 
-static void
+static float
 gistpenalty(GISTSTATE *giststate, int attno,
-           GISTENTRY *key1, bool isNull1,
-           GISTENTRY *key2, bool isNull2, float *penalty)
+           GISTENTRY *orig, bool isNullOrig,
+           GISTENTRY *add, bool isNullAdd)
 {
-   if (giststate->penaltyFn[attno].fn_strict && (isNull1 || isNull2))
-       *penalty = 0.0;
-   else
+   float penalty = 0.0;
+
+   if ( giststate->penaltyFn[attno].fn_strict==FALSE || ( isNullOrig == FALSE && isNullAdd == FALSE ) ) 
        FunctionCall3(&giststate->penaltyFn[attno],
-                     PointerGetDatum(key1),
-                     PointerGetDatum(key2),
-                     PointerGetDatum(penalty));
+                     PointerGetDatum(orig),
+                     PointerGetDatum(add),
+                     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
-gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
+gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GIST_SPLITVEC *v,
                  IndexTuple *itup, int len, GISTSTATE *giststate)
 {
    /*
     * now let the user-defined picksplit function set up the split vector; in
     * entryvec have no null value!!
     */
-   FunctionCall2(&giststate->picksplitFn[0],
+   FunctionCall2(&giststate->picksplitFn[attno],
                  PointerGetDatum(entryvec),
                  PointerGetDatum(v));
 
@@ -856,16 +776,16 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
    if (v->spl_right[v->spl_nright - 1] == InvalidOffsetNumber)
        v->spl_right[v->spl_nright - 1] = (OffsetNumber) (entryvec->n - 1);
 
-   v->spl_lattr[0] = v->spl_ldatum;
-   v->spl_rattr[0] = v->spl_rdatum;
-   v->spl_lisnull[0] = false;
-   v->spl_risnull[0] = false;
+   v->spl_lattr[attno] = v->spl_ldatum;
+   v->spl_rattr[attno] = v->spl_rdatum;
+   v->spl_lisnull[attno] = false;
+   v->spl_risnull[attno] = false;
 
    /*
     * if index is multikey, then we must to try get smaller bounding box for
     * subkey(s)
     */
-   if (giststate->tupdesc->natts > 1)
+   if (giststate->tupdesc->natts > 1 && attno+1 != giststate->tupdesc->natts)
    {
        int         MaxGrpId;
 
@@ -873,17 +793,17 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, GIST_SPLITVEC *v,
        v->spl_grpflag = (char *) palloc0(sizeof(char) * 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) */
-       gistunionsubkey(giststate, itup, v);
+       gistunionsubkey(giststate, itup, v, attno + 1);
 
        /*
         * if possible, we insert equivalent tuples with control by penalty
         * for a subkey(s)
         */
        if (MaxGrpId > 1)
-           gistadjsubkey(r, itup, len, v, giststate);
+           gistadjsubkey(r, itup, len, v, giststate, attno);
    }
 }
 
index f08d49dbf90df21c77d4dd74131407970b97d70a..43a6f62943ab511da38a7524c68e1f0e515f9400 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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
 /* root page of a gist index */
 #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) \
        ( \
                (isnull) ? 0 : \
@@ -291,12 +280,6 @@ extern IndexTuple gistgetadjusted(Relation r,
                IndexTuple oldtup,
                IndexTuple addtup,
                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,
              Relation r, Datum *attdata, int *datumsize, bool *isnull);
 
@@ -321,13 +304,15 @@ typedef struct {
 } GistSplitVec;
 
 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 gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
               Datum k, Relation r, Page pg, OffsetNumber o,
               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);
 
 /* gistvacuum.c */
index 17ce73d015fab151b817f7e021ba2e4a3e37349e..06f0a125bfdfc0f3b5f3b52d405ad52ddb1f1bab 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200605191
+#define CATALOG_VERSION_NO 200605241
 
 #endif
index 07a74c892d149aff97d6c8a65506483972aaef9c..141b42e02d71dd18d95d2fe8dc0b06fbb2733f56 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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
  *     the genbki.sh script reads this file and generates .bki
@@ -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 ));
 DESCR("hash index access method");
 #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");
 #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 ));