~ubuntu-branches/ubuntu/precise/postgresql-9.1/precise-security

« back to all changes in this revision

Viewing changes to src/backend/access/gist/gistutil.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * gistutil.c
 
4
 *        utilities routines for the postgres GiST index access method.
 
5
 *
 
6
 *
 
7
 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
 
8
 * Portions Copyright (c) 1994, Regents of the University of California
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *                      src/backend/access/gist/gistutil.c
 
12
 *-------------------------------------------------------------------------
 
13
 */
 
14
#include "postgres.h"
 
15
 
 
16
#include "access/gist_private.h"
 
17
#include "access/reloptions.h"
 
18
#include "storage/freespace.h"
 
19
#include "storage/indexfsm.h"
 
20
#include "storage/lmgr.h"
 
21
#include "storage/bufmgr.h"
 
22
#include "utils/rel.h"
 
23
 
 
24
/*
 
25
 * static *S used for temrorary storage (saves stack and palloc() call)
 
26
 */
 
27
 
 
28
static Datum attrS[INDEX_MAX_KEYS];
 
29
static bool isnullS[INDEX_MAX_KEYS];
 
30
 
 
31
/*
 
32
 * Write itup vector to page, has no control of free space.
 
33
 */
 
34
void
 
35
gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
 
36
{
 
37
        OffsetNumber l = InvalidOffsetNumber;
 
38
        int                     i;
 
39
 
 
40
        if (off == InvalidOffsetNumber)
 
41
                off = (PageIsEmpty(page)) ? FirstOffsetNumber :
 
42
                        OffsetNumberNext(PageGetMaxOffsetNumber(page));
 
43
 
 
44
        for (i = 0; i < len; i++)
 
45
        {
 
46
                Size            sz = IndexTupleSize(itup[i]);
 
47
 
 
48
                l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
 
49
                if (l == InvalidOffsetNumber)
 
50
                        elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
 
51
                                 i, len, (int) sz);
 
52
                off++;
 
53
        }
 
54
}
 
55
 
 
56
/*
 
57
 * Check space for itup vector on page
 
58
 */
 
59
bool
 
60
gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
 
61
{
 
62
        unsigned int size = freespace,
 
63
                                deleted = 0;
 
64
        int                     i;
 
65
 
 
66
        for (i = 0; i < len; i++)
 
67
                size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
 
68
 
 
69
        if (todelete != InvalidOffsetNumber)
 
70
        {
 
71
                IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
 
72
 
 
73
                deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
 
74
        }
 
75
 
 
76
        return (PageGetFreeSpace(page) + deleted < size);
 
77
}
 
78
 
 
79
bool
 
80
gistfitpage(IndexTuple *itvec, int len)
 
81
{
 
82
        int                     i;
 
83
        Size            size = 0;
 
84
 
 
85
        for (i = 0; i < len; i++)
 
86
                size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
 
87
 
 
88
        /* TODO: Consider fillfactor */
 
89
        return (size <= GiSTPageSize);
 
90
}
 
91
 
 
92
/*
 
93
 * Read buffer into itup vector
 
94
 */
 
95
IndexTuple *
 
96
gistextractpage(Page page, int *len /* out */ )
 
97
{
 
98
        OffsetNumber i,
 
99
                                maxoff;
 
100
        IndexTuple *itvec;
 
101
 
 
102
        maxoff = PageGetMaxOffsetNumber(page);
 
103
        *len = maxoff;
 
104
        itvec = palloc(sizeof(IndexTuple) * maxoff);
 
105
        for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
 
106
                itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
 
107
 
 
108
        return itvec;
 
109
}
 
110
 
 
111
/*
 
112
 * join two vectors into one
 
113
 */
 
114
IndexTuple *
 
115
gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
 
116
{
 
117
        itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen));
 
118
        memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
 
119
        *len += addlen;
 
120
        return itvec;
 
121
}
 
122
 
 
123
/*
 
124
 * make plain IndexTupleVector
 
125
 */
 
126
 
 
127
IndexTupleData *
 
128
gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
 
129
{
 
130
        char       *ptr,
 
131
                           *ret;
 
132
        int                     i;
 
133
 
 
134
        *memlen = 0;
 
135
 
 
136
        for (i = 0; i < veclen; i++)
 
137
                *memlen += IndexTupleSize(vec[i]);
 
138
 
 
139
        ptr = ret = palloc(*memlen);
 
140
 
 
141
        for (i = 0; i < veclen; i++)
 
142
        {
 
143
                memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
 
144
                ptr += IndexTupleSize(vec[i]);
 
145
        }
 
146
 
 
147
        return (IndexTupleData *) ret;
 
148
}
 
149
 
 
150
/*
 
151
 * Make unions of keys in IndexTuple vector, return FALSE if itvec contains
 
152
 * invalid tuple. Resulting Datums aren't compressed.
 
153
 */
 
154
 
 
155
void
 
156
gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, int startkey,
 
157
                                   Datum *attr, bool *isnull)
 
158
{
 
159
        int                     i;
 
160
        GistEntryVector *evec;
 
161
        int                     attrsize;
 
162
 
 
163
        evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
 
164
 
 
165
        for (i = startkey; i < giststate->tupdesc->natts; i++)
 
166
        {
 
167
                int                     j;
 
168
 
 
169
                evec->n = 0;
 
170
                if (!isnull[i])
 
171
                {
 
172
                        gistentryinit(evec->vector[evec->n], attr[i],
 
173
                                                  NULL, NULL, (OffsetNumber) 0,
 
174
                                                  FALSE);
 
175
                        evec->n++;
 
176
                }
 
177
 
 
178
                for (j = 0; j < len; j++)
 
179
                {
 
180
                        Datum           datum;
 
181
                        bool            IsNull;
 
182
 
 
183
                        datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull);
 
184
                        if (IsNull)
 
185
                                continue;
 
186
 
 
187
                        gistdentryinit(giststate, i,
 
188
                                                   evec->vector + evec->n,
 
189
                                                   datum,
 
190
                                                   NULL, NULL, (OffsetNumber) 0,
 
191
                                                   FALSE, IsNull);
 
192
                        evec->n++;
 
193
                }
 
194
 
 
195
                /* If this tuple vector was all NULLs, the union is NULL */
 
196
                if (evec->n == 0)
 
197
                {
 
198
                        attr[i] = (Datum) 0;
 
199
                        isnull[i] = TRUE;
 
200
                }
 
201
                else
 
202
                {
 
203
                        if (evec->n == 1)
 
204
                        {
 
205
                                evec->n = 2;
 
206
                                evec->vector[1] = evec->vector[0];
 
207
                        }
 
208
 
 
209
                        /* Make union and store in attr array */
 
210
                        attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
 
211
                                                                                giststate->supportCollation[i],
 
212
                                                                                PointerGetDatum(evec),
 
213
                                                                                PointerGetDatum(&attrsize));
 
214
 
 
215
                        isnull[i] = FALSE;
 
216
                }
 
217
        }
 
218
}
 
219
 
 
220
/*
 
221
 * Return an IndexTuple containing the result of applying the "union"
 
222
 * method to the specified IndexTuple vector.
 
223
 */
 
224
IndexTuple
 
225
gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 
226
{
 
227
        memset(isnullS, TRUE, sizeof(bool) * giststate->tupdesc->natts);
 
228
 
 
229
        gistMakeUnionItVec(giststate, itvec, len, 0, attrS, isnullS);
 
230
 
 
231
        return gistFormTuple(giststate, r, attrS, isnullS, false);
 
232
}
 
233
 
 
234
/*
 
235
 * makes union of two key
 
236
 */
 
237
void
 
238
gistMakeUnionKey(GISTSTATE *giststate, int attno,
 
239
                                 GISTENTRY *entry1, bool isnull1,
 
240
                                 GISTENTRY *entry2, bool isnull2,
 
241
                                 Datum *dst, bool *dstisnull)
 
242
{
 
243
 
 
244
        int                     dstsize;
 
245
 
 
246
        static char storage[2 * sizeof(GISTENTRY) + GEVHDRSZ];
 
247
        GistEntryVector *evec = (GistEntryVector *) storage;
 
248
 
 
249
        evec->n = 2;
 
250
 
 
251
        if (isnull1 && isnull2)
 
252
        {
 
253
                *dstisnull = TRUE;
 
254
                *dst = (Datum) 0;
 
255
        }
 
256
        else
 
257
        {
 
258
                if (isnull1 == FALSE && isnull2 == FALSE)
 
259
                {
 
260
                        evec->vector[0] = *entry1;
 
261
                        evec->vector[1] = *entry2;
 
262
                }
 
263
                else if (isnull1 == FALSE)
 
264
                {
 
265
                        evec->vector[0] = *entry1;
 
266
                        evec->vector[1] = *entry1;
 
267
                }
 
268
                else
 
269
                {
 
270
                        evec->vector[0] = *entry2;
 
271
                        evec->vector[1] = *entry2;
 
272
                }
 
273
 
 
274
                *dstisnull = FALSE;
 
275
                *dst = FunctionCall2Coll(&giststate->unionFn[attno],
 
276
                                                                 giststate->supportCollation[attno],
 
277
                                                                 PointerGetDatum(evec),
 
278
                                                                 PointerGetDatum(&dstsize));
 
279
        }
 
280
}
 
281
 
 
282
bool
 
283
gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
 
284
{
 
285
        bool            result;
 
286
 
 
287
        FunctionCall3Coll(&giststate->equalFn[attno],
 
288
                                          giststate->supportCollation[attno],
 
289
                                          a, b,
 
290
                                          PointerGetDatum(&result));
 
291
        return result;
 
292
}
 
293
 
 
294
/*
 
295
 * Decompress all keys in tuple
 
296
 */
 
297
void
 
298
gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
 
299
                                  OffsetNumber o, GISTENTRY *attdata, bool *isnull)
 
300
{
 
301
        int                     i;
 
302
 
 
303
        for (i = 0; i < r->rd_att->natts; i++)
 
304
        {
 
305
                Datum           datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
 
306
 
 
307
                gistdentryinit(giststate, i, &attdata[i],
 
308
                                           datum, r, p, o,
 
309
                                           FALSE, isnull[i]);
 
310
        }
 
311
}
 
312
 
 
313
/*
 
314
 * Forms union of oldtup and addtup, if union == oldtup then return NULL
 
315
 */
 
316
IndexTuple
 
317
gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 
318
{
 
319
        bool            neednew = FALSE;
 
320
        GISTENTRY       oldentries[INDEX_MAX_KEYS],
 
321
                                addentries[INDEX_MAX_KEYS];
 
322
        bool            oldisnull[INDEX_MAX_KEYS],
 
323
                                addisnull[INDEX_MAX_KEYS];
 
324
        IndexTuple      newtup = NULL;
 
325
        int                     i;
 
326
 
 
327
        gistDeCompressAtt(giststate, r, oldtup, NULL,
 
328
                                          (OffsetNumber) 0, oldentries, oldisnull);
 
329
 
 
330
        gistDeCompressAtt(giststate, r, addtup, NULL,
 
331
                                          (OffsetNumber) 0, addentries, addisnull);
 
332
 
 
333
        for (i = 0; i < r->rd_att->natts; i++)
 
334
        {
 
335
                gistMakeUnionKey(giststate, i,
 
336
                                                 oldentries + i, oldisnull[i],
 
337
                                                 addentries + i, addisnull[i],
 
338
                                                 attrS + i, isnullS + i);
 
339
 
 
340
                if (neednew)
 
341
                        /* we already need new key, so we can skip check */
 
342
                        continue;
 
343
 
 
344
                if (isnullS[i])
 
345
                        /* union of key may be NULL if and only if both keys are NULL */
 
346
                        continue;
 
347
 
 
348
                if (!addisnull[i])
 
349
                {
 
350
                        if (oldisnull[i] || gistKeyIsEQ(giststate, i, oldentries[i].key, attrS[i]) == false)
 
351
                                neednew = true;
 
352
                }
 
353
        }
 
354
 
 
355
        if (neednew)
 
356
        {
 
357
                /* need to update key */
 
358
                newtup = gistFormTuple(giststate, r, attrS, isnullS, false);
 
359
                newtup->t_tid = oldtup->t_tid;
 
360
        }
 
361
 
 
362
        return newtup;
 
363
}
 
364
 
 
365
/*
 
366
 * find entry with lowest penalty
 
367
 */
 
368
OffsetNumber
 
369
gistchoose(Relation r, Page p, IndexTuple it,   /* it has compressed entry */
 
370
                   GISTSTATE *giststate)
 
371
{
 
372
        OffsetNumber maxoff;
 
373
        OffsetNumber i;
 
374
        OffsetNumber which;
 
375
        float           sum_grow,
 
376
                                which_grow[INDEX_MAX_KEYS];
 
377
        GISTENTRY       entry,
 
378
                                identry[INDEX_MAX_KEYS];
 
379
        bool            isnull[INDEX_MAX_KEYS];
 
380
 
 
381
        maxoff = PageGetMaxOffsetNumber(p);
 
382
        *which_grow = -1.0;
 
383
        which = InvalidOffsetNumber;
 
384
        sum_grow = 1;
 
385
        gistDeCompressAtt(giststate, r,
 
386
                                          it, NULL, (OffsetNumber) 0,
 
387
                                          identry, isnull);
 
388
 
 
389
        Assert(maxoff >= FirstOffsetNumber);
 
390
        Assert(!GistPageIsLeaf(p));
 
391
 
 
392
        for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i))
 
393
        {
 
394
                int                     j;
 
395
                IndexTuple      itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
 
396
 
 
397
                sum_grow = 0;
 
398
                for (j = 0; j < r->rd_att->natts; j++)
 
399
                {
 
400
                        Datum           datum;
 
401
                        float           usize;
 
402
                        bool            IsNull;
 
403
 
 
404
                        datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
 
405
                        gistdentryinit(giststate, j, &entry, datum, r, p, i,
 
406
                                                   FALSE, IsNull);
 
407
                        usize = gistpenalty(giststate, j, &entry, IsNull,
 
408
                                                                &identry[j], isnull[j]);
 
409
 
 
410
                        if (which_grow[j] < 0 || usize < which_grow[j])
 
411
                        {
 
412
                                which = i;
 
413
                                which_grow[j] = usize;
 
414
                                if (j < r->rd_att->natts - 1 && i == FirstOffsetNumber)
 
415
                                        which_grow[j + 1] = -1;
 
416
                                sum_grow += which_grow[j];
 
417
                        }
 
418
                        else if (which_grow[j] == usize)
 
419
                                sum_grow += usize;
 
420
                        else
 
421
                        {
 
422
                                sum_grow = 1;
 
423
                                break;
 
424
                        }
 
425
                }
 
426
        }
 
427
 
 
428
        if (which == InvalidOffsetNumber)
 
429
                which = FirstOffsetNumber;
 
430
 
 
431
        return which;
 
432
}
 
433
 
 
434
/*
 
435
 * initialize a GiST entry with a decompressed version of key
 
436
 */
 
437
void
 
438
gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 
439
                           Datum k, Relation r, Page pg, OffsetNumber o,
 
440
                           bool l, bool isNull)
 
441
{
 
442
        if (!isNull)
 
443
        {
 
444
                GISTENTRY  *dep;
 
445
 
 
446
                gistentryinit(*e, k, r, pg, o, l);
 
447
                dep = (GISTENTRY *)
 
448
                        DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
 
449
                                                                                          giststate->supportCollation[nkey],
 
450
                                                                                          PointerGetDatum(e)));
 
451
                /* decompressFn may just return the given pointer */
 
452
                if (dep != e)
 
453
                        gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
 
454
                                                  dep->leafkey);
 
455
        }
 
456
        else
 
457
                gistentryinit(*e, (Datum) 0, r, pg, o, l);
 
458
}
 
459
 
 
460
 
 
461
/*
 
462
 * initialize a GiST entry with a compressed version of key
 
463
 */
 
464
void
 
465
gistcentryinit(GISTSTATE *giststate, int nkey,
 
466
                           GISTENTRY *e, Datum k, Relation r,
 
467
                           Page pg, OffsetNumber o, bool l, bool isNull)
 
468
{
 
469
        if (!isNull)
 
470
        {
 
471
                GISTENTRY  *cep;
 
472
 
 
473
                gistentryinit(*e, k, r, pg, o, l);
 
474
                cep = (GISTENTRY *)
 
475
                        DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[nkey],
 
476
                                                                                          giststate->supportCollation[nkey],
 
477
                                                                                          PointerGetDatum(e)));
 
478
                /* compressFn may just return the given pointer */
 
479
                if (cep != e)
 
480
                        gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset,
 
481
                                                  cep->leafkey);
 
482
        }
 
483
        else
 
484
                gistentryinit(*e, (Datum) 0, r, pg, o, l);
 
485
}
 
486
 
 
487
IndexTuple
 
488
gistFormTuple(GISTSTATE *giststate, Relation r,
 
489
                          Datum attdata[], bool isnull[], bool newValues)
 
490
{
 
491
        GISTENTRY       centry[INDEX_MAX_KEYS];
 
492
        Datum           compatt[INDEX_MAX_KEYS];
 
493
        int                     i;
 
494
        IndexTuple      res;
 
495
 
 
496
        for (i = 0; i < r->rd_att->natts; i++)
 
497
        {
 
498
                if (isnull[i])
 
499
                        compatt[i] = (Datum) 0;
 
500
                else
 
501
                {
 
502
                        gistcentryinit(giststate, i, &centry[i], attdata[i],
 
503
                                                   r, NULL, (OffsetNumber) 0,
 
504
                                                   newValues,
 
505
                                                   FALSE);
 
506
                        compatt[i] = centry[i].key;
 
507
                }
 
508
        }
 
509
 
 
510
        res = index_form_tuple(giststate->tupdesc, compatt, isnull);
 
511
 
 
512
        /*
 
513
         * The offset number on tuples on internal pages is unused. For historical
 
514
         * reasons, it is set 0xffff.
 
515
         */
 
516
        ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
 
517
        return res;
 
518
}
 
519
 
 
520
float
 
521
gistpenalty(GISTSTATE *giststate, int attno,
 
522
                        GISTENTRY *orig, bool isNullOrig,
 
523
                        GISTENTRY *add, bool isNullAdd)
 
524
{
 
525
        float           penalty = 0.0;
 
526
 
 
527
        if (giststate->penaltyFn[attno].fn_strict == FALSE ||
 
528
                (isNullOrig == FALSE && isNullAdd == FALSE))
 
529
                FunctionCall3Coll(&giststate->penaltyFn[attno],
 
530
                                                  giststate->supportCollation[attno],
 
531
                                                  PointerGetDatum(orig),
 
532
                                                  PointerGetDatum(add),
 
533
                                                  PointerGetDatum(&penalty));
 
534
        else if (isNullOrig && isNullAdd)
 
535
                penalty = 0.0;
 
536
        else
 
537
                penalty = 1e10;                 /* try to prevent to mix null and non-null
 
538
                                                                 * value */
 
539
 
 
540
        return penalty;
 
541
}
 
542
 
 
543
/*
 
544
 * Initialize a new index page
 
545
 */
 
546
void
 
547
GISTInitBuffer(Buffer b, uint32 f)
 
548
{
 
549
        GISTPageOpaque opaque;
 
550
        Page            page;
 
551
        Size            pageSize;
 
552
 
 
553
        pageSize = BufferGetPageSize(b);
 
554
        page = BufferGetPage(b);
 
555
        PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
 
556
 
 
557
        opaque = GistPageGetOpaque(page);
 
558
        /* page was already zeroed by PageInit, so this is not needed: */
 
559
        /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
 
560
        opaque->rightlink = InvalidBlockNumber;
 
561
        opaque->flags = f;
 
562
        opaque->gist_page_id = GIST_PAGE_ID;
 
563
}
 
564
 
 
565
/*
 
566
 * Verify that a freshly-read page looks sane.
 
567
 */
 
568
void
 
569
gistcheckpage(Relation rel, Buffer buf)
 
570
{
 
571
        Page            page = BufferGetPage(buf);
 
572
 
 
573
        /*
 
574
         * ReadBuffer verifies that every newly-read page passes
 
575
         * PageHeaderIsValid, which means it either contains a reasonably sane
 
576
         * page header or is all-zero.  We have to defend against the all-zero
 
577
         * case, however.
 
578
         */
 
579
        if (PageIsNew(page))
 
580
                ereport(ERROR,
 
581
                                (errcode(ERRCODE_INDEX_CORRUPTED),
 
582
                         errmsg("index \"%s\" contains unexpected zero page at block %u",
 
583
                                        RelationGetRelationName(rel),
 
584
                                        BufferGetBlockNumber(buf)),
 
585
                                 errhint("Please REINDEX it.")));
 
586
 
 
587
        /*
 
588
         * Additionally check that the special area looks sane.
 
589
         */
 
590
        if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
 
591
                ereport(ERROR,
 
592
                                (errcode(ERRCODE_INDEX_CORRUPTED),
 
593
                                 errmsg("index \"%s\" contains corrupted page at block %u",
 
594
                                                RelationGetRelationName(rel),
 
595
                                                BufferGetBlockNumber(buf)),
 
596
                                 errhint("Please REINDEX it.")));
 
597
}
 
598
 
 
599
 
 
600
/*
 
601
 * Allocate a new page (either by recycling, or by extending the index file)
 
602
 *
 
603
 * The returned buffer is already pinned and exclusive-locked
 
604
 *
 
605
 * Caller is responsible for initializing the page by calling GISTInitBuffer
 
606
 */
 
607
Buffer
 
608
gistNewBuffer(Relation r)
 
609
{
 
610
        Buffer          buffer;
 
611
        bool            needLock;
 
612
 
 
613
        /* First, try to get a page from FSM */
 
614
        for (;;)
 
615
        {
 
616
                BlockNumber blkno = GetFreeIndexPage(r);
 
617
 
 
618
                if (blkno == InvalidBlockNumber)
 
619
                        break;                          /* nothing left in FSM */
 
620
 
 
621
                buffer = ReadBuffer(r, blkno);
 
622
 
 
623
                /*
 
624
                 * We have to guard against the possibility that someone else already
 
625
                 * recycled this page; the buffer may be locked if so.
 
626
                 */
 
627
                if (ConditionalLockBuffer(buffer))
 
628
                {
 
629
                        Page            page = BufferGetPage(buffer);
 
630
 
 
631
                        if (PageIsNew(page))
 
632
                                return buffer;  /* OK to use, if never initialized */
 
633
 
 
634
                        gistcheckpage(r, buffer);
 
635
 
 
636
                        if (GistPageIsDeleted(page))
 
637
                                return buffer;  /* OK to use */
 
638
 
 
639
                        LockBuffer(buffer, GIST_UNLOCK);
 
640
                }
 
641
 
 
642
                /* Can't use it, so release buffer and try again */
 
643
                ReleaseBuffer(buffer);
 
644
        }
 
645
 
 
646
        /* Must extend the file */
 
647
        needLock = !RELATION_IS_LOCAL(r);
 
648
 
 
649
        if (needLock)
 
650
                LockRelationForExtension(r, ExclusiveLock);
 
651
 
 
652
        buffer = ReadBuffer(r, P_NEW);
 
653
        LockBuffer(buffer, GIST_EXCLUSIVE);
 
654
 
 
655
        if (needLock)
 
656
                UnlockRelationForExtension(r, ExclusiveLock);
 
657
 
 
658
        return buffer;
 
659
}
 
660
 
 
661
Datum
 
662
gistoptions(PG_FUNCTION_ARGS)
 
663
{
 
664
        Datum           reloptions = PG_GETARG_DATUM(0);
 
665
        bool            validate = PG_GETARG_BOOL(1);
 
666
        bytea      *result;
 
667
 
 
668
        result = default_reloptions(reloptions, validate, RELOPT_KIND_GIST);
 
669
 
 
670
        if (result)
 
671
                PG_RETURN_BYTEA_P(result);
 
672
        PG_RETURN_NULL();
 
673
}
 
674
 
 
675
/*
 
676
 * Temporary GiST indexes are not WAL-logged, but we need LSNs to detect
 
677
 * concurrent page splits anyway. GetXLogRecPtrForTemp() provides a fake
 
678
 * sequence of LSNs for that purpose. Each call generates an LSN that is
 
679
 * greater than any previous value returned by this function in the same
 
680
 * session.
 
681
 */
 
682
XLogRecPtr
 
683
GetXLogRecPtrForTemp(void)
 
684
{
 
685
        static XLogRecPtr counter = {0, 1};
 
686
 
 
687
        counter.xrecoff++;
 
688
        if (counter.xrecoff == 0)
 
689
        {
 
690
                counter.xlogid++;
 
691
                counter.xrecoff++;
 
692
        }
 
693
        return counter;
 
694
}