mirror of https://github.com/postgres/postgres
recovery after crash (power loss etc) it may say that it can't restore index and index should be reindexed. Some refactoring code.REL8_1_STABLE
parent
d6636543c4
commit
37c839365c
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,785 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* gistutil.c |
||||
* utilities routines for the postgres GiST index access method. |
||||
* |
||||
* |
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.1 2005/06/14 11:45:13 teodor Exp $ |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/genam.h" |
||||
#include "access/gist_private.h" |
||||
#include "access/gistscan.h" |
||||
#include "access/heapam.h" |
||||
#include "catalog/index.h" |
||||
#include "miscadmin.h" |
||||
|
||||
/* group flags ( in gistadjsubkey ) */ |
||||
#define LEFT_ADDED 0x01 |
||||
#define RIGHT_ADDED 0x02 |
||||
#define BOTH_ADDED ( LEFT_ADDED | RIGHT_ADDED ) |
||||
|
||||
|
||||
/*
|
||||
* This defines is only for shorter code, used in gistgetadjusted |
||||
* and gistadjsubkey only |
||||
*/ |
||||
#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); |
||||
|
||||
/*
|
||||
* Write itup vector to page, has no control of free space |
||||
*/ |
||||
OffsetNumber |
||||
gistfillbuffer(Relation r, Page page, IndexTuple *itup, |
||||
int len, OffsetNumber off) |
||||
{ |
||||
OffsetNumber l = InvalidOffsetNumber; |
||||
int i; |
||||
|
||||
for (i = 0; i < len; i++) |
||||
{ |
||||
l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]), |
||||
off, LP_USED); |
||||
if (l == InvalidOffsetNumber) |
||||
elog(ERROR, "gistfillbuffer: failed to add index item to \"%s\"", |
||||
RelationGetRelationName(r)); |
||||
off++; |
||||
} |
||||
return l; |
||||
} |
||||
|
||||
/*
|
||||
* Check space for itup vector on page |
||||
*/ |
||||
bool |
||||
gistnospace(Page page, IndexTuple *itvec, int len) |
||||
{ |
||||
unsigned int size = 0; |
||||
int i; |
||||
|
||||
for (i = 0; i < len; i++) |
||||
size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData); |
||||
|
||||
return (PageGetFreeSpace(page) < size); |
||||
} |
||||
|
||||
/*
|
||||
* Read buffer into itup vector |
||||
*/ |
||||
IndexTuple * |
||||
gistextractbuffer(Buffer buffer, int *len /* out */ ) |
||||
{ |
||||
OffsetNumber i, |
||||
maxoff; |
||||
IndexTuple *itvec; |
||||
Page p = (Page) BufferGetPage(buffer); |
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p); |
||||
*len = maxoff; |
||||
itvec = palloc(sizeof(IndexTuple) * maxoff); |
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
||||
itvec[i - 1] = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); |
||||
|
||||
return itvec; |
||||
} |
||||
|
||||
/*
|
||||
* join two vectors into one |
||||
*/ |
||||
IndexTuple * |
||||
gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen) |
||||
{ |
||||
itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen)); |
||||
memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen); |
||||
*len += addlen; |
||||
return itvec; |
||||
} |
||||
|
||||
/*
|
||||
* 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) |
||||
{ |
||||
Datum attr[INDEX_MAX_KEYS]; |
||||
bool isnull[INDEX_MAX_KEYS]; |
||||
GistEntryVector *evec; |
||||
int i; |
||||
GISTENTRY centry[INDEX_MAX_KEYS]; |
||||
|
||||
evec = (GistEntryVector *) palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); |
||||
|
||||
for (i = 0; i < r->rd_att->natts; i++) |
||||
{ |
||||
Datum datum; |
||||
int j; |
||||
int real_len; |
||||
|
||||
real_len = 0; |
||||
for (j = 0; j < len; j++) |
||||
{ |
||||
bool IsNull; |
||||
datum = index_getattr(itvec[j], 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 this tuple vector was all NULLs, the union is NULL */ |
||||
if (real_len == 0) |
||||
{ |
||||
attr[i] = (Datum) 0; |
||||
isnull[i] = TRUE; |
||||
} |
||||
else |
||||
{ |
||||
int datumsize; |
||||
|
||||
if (real_len == 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; |
||||
|
||||
/* Compress the result of the union and store in attr array */ |
||||
datum = FunctionCall2(&giststate->unionFn[i], |
||||
PointerGetDatum(evec), |
||||
PointerGetDatum(&datumsize)); |
||||
|
||||
gistcentryinit(giststate, i, ¢ry[i], datum, |
||||
NULL, NULL, (OffsetNumber) 0, |
||||
datumsize, FALSE, FALSE); |
||||
isnull[i] = FALSE; |
||||
attr[i] = centry[i].key; |
||||
} |
||||
} |
||||
|
||||
return index_form_tuple(giststate->tupdesc, attr, isnull); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* Forms union of oldtup and addtup, if union == oldtup then return NULL |
||||
*/ |
||||
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 oldisnull[INDEX_MAX_KEYS], |
||||
addisnull[INDEX_MAX_KEYS]; |
||||
IndexTuple newtup = NULL; |
||||
int i; |
||||
|
||||
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); |
||||
|
||||
gistDeCompressAtt(giststate, r, addtup, NULL, |
||||
(OffsetNumber) 0, addatt, 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; |
||||
|
||||
FILLEV(oldisnull[i], oldatt[i].key, oldatt[i].bytes, |
||||
addisnull[i], addatt[i].key, addatt[i].bytes); |
||||
|
||||
datum = FunctionCall2(&giststate->unionFn[i], |
||||
PointerGetDatum(evec), |
||||
PointerGetDatum(&datumsize)); |
||||
|
||||
if (oldisnull[i] || addisnull[i]) |
||||
{ |
||||
if (oldisnull[i]) |
||||
neednew = true; |
||||
} |
||||
else |
||||
{ |
||||
bool result; |
||||
|
||||
FunctionCall3(&giststate->equalFn[i], |
||||
ev0p->key, |
||||
datum, |
||||
PointerGetDatum(&result)); |
||||
|
||||
if (!result) |
||||
neednew = true; |
||||
} |
||||
|
||||
gistcentryinit(giststate, i, ¢ry[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->t_tid = oldtup->t_tid; |
||||
} |
||||
|
||||
return newtup; |
||||
} |
||||
|
||||
void |
||||
gistunionsubkey(Relation r, GISTSTATE *giststate, IndexTuple *itvec, GIST_SPLITVEC *spl) |
||||
{ |
||||
int lr; |
||||
|
||||
for (lr = 0; lr < 2; lr++) |
||||
{ |
||||
OffsetNumber *entries; |
||||
int i; |
||||
Datum *attr; |
||||
int len, |
||||
*attrsize; |
||||
bool *isnull; |
||||
GistEntryVector *evec; |
||||
|
||||
if (lr) |
||||
{ |
||||
attrsize = spl->spl_lattrsize; |
||||
attr = spl->spl_lattr; |
||||
len = spl->spl_nleft; |
||||
entries = spl->spl_left; |
||||
isnull = spl->spl_lisnull; |
||||
} |
||||
else |
||||
{ |
||||
attrsize = spl->spl_rattrsize; |
||||
attr = spl->spl_rattr; |
||||
len = spl->spl_nright; |
||||
entries = spl->spl_right; |
||||
isnull = spl->spl_risnull; |
||||
} |
||||
|
||||
evec = palloc(((len == 1) ? 2 : len) * sizeof(GISTENTRY) + GEVHDRSZ); |
||||
|
||||
for (i = 1; i < r->rd_att->natts; i++) |
||||
{ |
||||
int j; |
||||
Datum datum; |
||||
int datumsize; |
||||
int real_len; |
||||
|
||||
real_len = 0; |
||||
for (j = 0; j < len; j++) |
||||
{ |
||||
bool IsNull; |
||||
|
||||
if (spl->spl_idgrp[entries[j]]) |
||||
continue; |
||||
datum = index_getattr(itvec[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++; |
||||
|
||||
} |
||||
|
||||
if (real_len == 0) |
||||
{ |
||||
datum = (Datum) 0; |
||||
datumsize = 0; |
||||
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)); |
||||
isnull[i] = false; |
||||
} |
||||
|
||||
attr[i] = datum; |
||||
attrsize[i] = datumsize; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* find group in vector with equal value |
||||
*/ |
||||
int |
||||
gistfindgroup(GISTSTATE *giststate, GISTENTRY *valvec, GIST_SPLITVEC *spl) |
||||
{ |
||||
int i; |
||||
int curid = 1; |
||||
|
||||
/*
|
||||
* first key is always not null (see gistinsert), so we may not check |
||||
* for nulls |
||||
*/ |
||||
for (i = 0; i < spl->spl_nleft; i++) |
||||
{ |
||||
int j; |
||||
int len; |
||||
bool result; |
||||
|
||||
if (spl->spl_idgrp[spl->spl_left[i]]) |
||||
continue; |
||||
len = 0; |
||||
/* find all equal value in right part */ |
||||
for (j = 0; j < spl->spl_nright; j++) |
||||
{ |
||||
if (spl->spl_idgrp[spl->spl_right[j]]) |
||||
continue; |
||||
FunctionCall3(&giststate->equalFn[0], |
||||
valvec[spl->spl_left[i]].key, |
||||
valvec[spl->spl_right[j]].key, |
||||
PointerGetDatum(&result)); |
||||
if (result) |
||||
{ |
||||
spl->spl_idgrp[spl->spl_right[j]] = curid; |
||||
len++; |
||||
} |
||||
} |
||||
/* find all other equal value in left part */ |
||||
if (len) |
||||
{ |
||||
/* add current val to list of equal values */ |
||||
spl->spl_idgrp[spl->spl_left[i]] = curid; |
||||
/* searching .. */ |
||||
for (j = i + 1; j < spl->spl_nleft; j++) |
||||
{ |
||||
if (spl->spl_idgrp[spl->spl_left[j]]) |
||||
continue; |
||||
FunctionCall3(&giststate->equalFn[0], |
||||
valvec[spl->spl_left[i]].key, |
||||
valvec[spl->spl_left[j]].key, |
||||
PointerGetDatum(&result)); |
||||
if (result) |
||||
{ |
||||
spl->spl_idgrp[spl->spl_left[j]] = curid; |
||||
len++; |
||||
} |
||||
} |
||||
spl->spl_ngrp[curid] = len + 1; |
||||
curid++; |
||||
} |
||||
} |
||||
|
||||
return curid; |
||||
} |
||||
|
||||
/*
|
||||
* Insert equivalent tuples to left or right page with minimum |
||||
* penalty |
||||
*/ |
||||
void |
||||
gistadjsubkey(Relation r, |
||||
IndexTuple *itup, /* contains compressed entry */ |
||||
int *len, |
||||
GIST_SPLITVEC *v, |
||||
GISTSTATE *giststate) |
||||
{ |
||||
int curlen; |
||||
OffsetNumber *curwpos; |
||||
GISTENTRY entry, |
||||
identry[INDEX_MAX_KEYS], |
||||
*ev0p, |
||||
*ev1p; |
||||
float lpenalty, |
||||
rpenalty; |
||||
GistEntryVector *evec; |
||||
int datumsize; |
||||
bool isnull[INDEX_MAX_KEYS]; |
||||
int i, |
||||
j; |
||||
|
||||
/* clear vectors */ |
||||
curlen = v->spl_nleft; |
||||
curwpos = v->spl_left; |
||||
for (i = 0; i < v->spl_nleft; i++) |
||||
{ |
||||
if (v->spl_idgrp[v->spl_left[i]] == 0) |
||||
{ |
||||
*curwpos = v->spl_left[i]; |
||||
curwpos++; |
||||
} |
||||
else |
||||
curlen--; |
||||
} |
||||
v->spl_nleft = curlen; |
||||
|
||||
curlen = v->spl_nright; |
||||
curwpos = v->spl_right; |
||||
for (i = 0; i < v->spl_nright; i++) |
||||
{ |
||||
if (v->spl_idgrp[v->spl_right[i]] == 0) |
||||
{ |
||||
*curwpos = v->spl_right[i]; |
||||
curwpos++; |
||||
} |
||||
else |
||||
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 */ |
||||
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, |
||||
identry, isnull); |
||||
|
||||
v->spl_ngrp[v->spl_idgrp[i + 1]]--; |
||||
if (v->spl_ngrp[v->spl_idgrp[i + 1]] == 0 && |
||||
(v->spl_grpflag[v->spl_idgrp[i + 1]] & BOTH_ADDED) != BOTH_ADDED) |
||||
{ |
||||
/* force last in group */ |
||||
rpenalty = 1.0; |
||||
lpenalty = (v->spl_grpflag[v->spl_idgrp[i + 1]] & LEFT_ADDED) ? 2.0 : 0.0; |
||||
} |
||||
else |
||||
{ |
||||
/* where? */ |
||||
for (j = 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); |
||||
|
||||
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); |
||||
|
||||
if (lpenalty != rpenalty) |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* add |
||||
* XXX: refactor this to avoid duplicating code |
||||
*/ |
||||
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); |
||||
|
||||
datum = FunctionCall2(&giststate->unionFn[j], |
||||
PointerGetDatum(evec), |
||||
PointerGetDatum(&datumsize)); |
||||
|
||||
v->spl_lattr[j] = datum; |
||||
v->spl_lattrsize[j] = datumsize; |
||||
v->spl_lisnull[j] = false; |
||||
} |
||||
} |
||||
} |
||||
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); |
||||
|
||||
datum = FunctionCall2(&giststate->unionFn[j], |
||||
PointerGetDatum(evec), |
||||
PointerGetDatum(&datumsize)); |
||||
|
||||
v->spl_rattr[j] = datum; |
||||
v->spl_rattrsize[j] = datumsize; |
||||
v->spl_risnull[j] = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* find entry with lowest penalty |
||||
*/ |
||||
OffsetNumber |
||||
gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ |
||||
GISTSTATE *giststate) |
||||
{ |
||||
OffsetNumber maxoff; |
||||
OffsetNumber i; |
||||
OffsetNumber which; |
||||
float sum_grow, |
||||
which_grow[INDEX_MAX_KEYS]; |
||||
GISTENTRY entry, |
||||
identry[INDEX_MAX_KEYS]; |
||||
bool isnull[INDEX_MAX_KEYS]; |
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p); |
||||
*which_grow = -1.0; |
||||
which = -1; |
||||
sum_grow = 1; |
||||
gistDeCompressAtt(giststate, r, |
||||
it, NULL, (OffsetNumber) 0, |
||||
identry, isnull); |
||||
|
||||
for (i = FirstOffsetNumber; i <= maxoff && sum_grow; i = OffsetNumberNext(i)) |
||||
{ |
||||
int j; |
||||
IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i)); |
||||
|
||||
sum_grow = 0; |
||||
for (j = 0; j < r->rd_att->natts; j++) |
||||
{ |
||||
Datum datum; |
||||
float usize; |
||||
bool IsNull; |
||||
|
||||
datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull); |
||||
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); |
||||
|
||||
if (which_grow[j] < 0 || usize < which_grow[j]) |
||||
{ |
||||
which = i; |
||||
which_grow[j] = usize; |
||||
if (j < r->rd_att->natts - 1 && i == FirstOffsetNumber) |
||||
which_grow[j + 1] = -1; |
||||
sum_grow += which_grow[j]; |
||||
} |
||||
else if (which_grow[j] == usize) |
||||
sum_grow += usize; |
||||
else |
||||
{ |
||||
sum_grow = 1; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return which; |
||||
} |
||||
|
||||
/*
|
||||
* initialize a GiST entry with a decompressed version of key |
||||
*/ |
||||
void |
||||
gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, |
||||
Datum k, Relation r, Page pg, OffsetNumber o, |
||||
int b, bool l, bool isNull) |
||||
{ |
||||
if (b && !isNull) |
||||
{ |
||||
GISTENTRY *dep; |
||||
|
||||
gistentryinit(*e, k, r, pg, o, b, l); |
||||
dep = (GISTENTRY *) |
||||
DatumGetPointer(FunctionCall1(&giststate->decompressFn[nkey], |
||||
PointerGetDatum(e))); |
||||
/* decompressFn may just return the given pointer */ |
||||
if (dep != e) |
||||
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset, |
||||
dep->bytes, dep->leafkey); |
||||
} |
||||
else |
||||
gistentryinit(*e, (Datum) 0, r, pg, o, 0, l); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* initialize a GiST entry with a compressed version of key |
||||
*/ |
||||
void |
||||
gistcentryinit(GISTSTATE *giststate, int nkey, |
||||
GISTENTRY *e, Datum k, Relation r, |
||||
Page pg, OffsetNumber o, int b, bool l, bool isNull) |
||||
{ |
||||
if (!isNull) |
||||
{ |
||||
GISTENTRY *cep; |
||||
|
||||
gistentryinit(*e, k, r, pg, o, b, l); |
||||
cep = (GISTENTRY *) |
||||
DatumGetPointer(FunctionCall1(&giststate->compressFn[nkey], |
||||
PointerGetDatum(e))); |
||||
/* compressFn may just return the given pointer */ |
||||
if (cep != e) |
||||
gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset, |
||||
cep->bytes, cep->leafkey); |
||||
} |
||||
else |
||||
gistentryinit(*e, (Datum) 0, r, pg, o, 0, l); |
||||
} |
||||
|
||||
IndexTuple |
||||
gistFormTuple(GISTSTATE *giststate, Relation r, |
||||
Datum attdata[], int datumsize[], bool isnull[]) |
||||
{ |
||||
GISTENTRY centry[INDEX_MAX_KEYS]; |
||||
Datum compatt[INDEX_MAX_KEYS]; |
||||
int i; |
||||
|
||||
for (i = 0; i < r->rd_att->natts; i++) |
||||
{ |
||||
if (isnull[i]) |
||||
compatt[i] = (Datum) 0; |
||||
else |
||||
{ |
||||
gistcentryinit(giststate, i, ¢ry[i], attdata[i], |
||||
NULL, NULL, (OffsetNumber) 0, |
||||
datumsize[i], FALSE, FALSE); |
||||
compatt[i] = centry[i].key; |
||||
} |
||||
} |
||||
|
||||
return index_form_tuple(giststate->tupdesc, compatt, isnull); |
||||
} |
||||
|
||||
void |
||||
gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, |
||||
OffsetNumber o, GISTENTRY *attdata, bool *isnull) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < r->rd_att->natts; i++) |
||||
{ |
||||
Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); |
||||
gistdentryinit(giststate, i, &attdata[i], |
||||
datum, r, p, o, |
||||
ATTSIZE(datum, giststate->tupdesc, i + 1, isnull[i]), |
||||
FALSE, isnull[i]); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
gistpenalty(GISTSTATE *giststate, int attno, |
||||
GISTENTRY *key1, bool isNull1, |
||||
GISTENTRY *key2, bool isNull2, float *penalty) |
||||
{ |
||||
if (giststate->penaltyFn[attno].fn_strict && (isNull1 || isNull2)) |
||||
*penalty = 0.0; |
||||
else |
||||
FunctionCall3(&giststate->penaltyFn[attno], |
||||
PointerGetDatum(key1), |
||||
PointerGetDatum(key2), |
||||
PointerGetDatum(penalty)); |
||||
} |
||||
|
||||
void |
||||
GISTInitBuffer(Buffer b, uint32 f) |
||||
{ |
||||
GISTPageOpaque opaque; |
||||
Page page; |
||||
Size pageSize; |
||||
|
||||
pageSize = BufferGetPageSize(b); |
||||
page = BufferGetPage(b); |
||||
PageInit(page, pageSize, sizeof(GISTPageOpaqueData)); |
||||
|
||||
opaque = (GISTPageOpaque) PageGetSpecialPointer(page); |
||||
opaque->flags = f; |
||||
} |
||||
|
||||
@ -0,0 +1,628 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* gistxlog.c |
||||
* WAL replay logic for GiST. |
||||
* |
||||
* |
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistxlog.c,v 1.1 2005/06/14 11:45:13 teodor Exp $ |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/genam.h" |
||||
#include "access/gist_private.h" |
||||
#include "access/gistscan.h" |
||||
#include "access/heapam.h" |
||||
#include "catalog/index.h" |
||||
#include "commands/vacuum.h" |
||||
#include "miscadmin.h" |
||||
#include "utils/memutils.h" |
||||
|
||||
typedef struct { |
||||
gistxlogEntryUpdate *data; |
||||
int len; |
||||
IndexTuple *itup; |
||||
BlockNumber *path; |
||||
} EntryUpdateRecord; |
||||
|
||||
typedef struct { |
||||
gistxlogPage *header; |
||||
OffsetNumber *offnum; |
||||
|
||||
/* to work with */ |
||||
Page page; |
||||
Buffer buffer; |
||||
bool is_ok; |
||||
} NewPage; |
||||
|
||||
typedef struct { |
||||
gistxlogPageSplit *data; |
||||
NewPage *page; |
||||
IndexTuple *itup; |
||||
BlockNumber *path; |
||||
} PageSplitRecord; |
||||
|
||||
/* track for incomplete inserts, idea was taken from nbtxlog.c */ |
||||
|
||||
typedef struct gistIncompleteInsert { |
||||
RelFileNode node; |
||||
ItemPointerData key; |
||||
int lenblk; |
||||
BlockNumber *blkno; |
||||
int pathlen; |
||||
BlockNumber *path; |
||||
} gistIncompleteInsert; |
||||
|
||||
|
||||
MemoryContext opCtx; |
||||
MemoryContext insertCtx; |
||||
static List *incomplete_inserts; |
||||
|
||||
|
||||
#define ItemPointerEQ( a, b ) \ |
||||
( \
|
||||
ItemPointerGetOffsetNumber(a) == ItemPointerGetOffsetNumber(a) && \
|
||||
ItemPointerGetBlockNumber (a) == ItemPointerGetBlockNumber(b) \
|
||||
) |
||||
|
||||
static void |
||||
pushIncompleteInsert(RelFileNode node, ItemPointerData key, |
||||
BlockNumber *blkno, int lenblk, |
||||
BlockNumber *path, int pathlen, |
||||
PageSplitRecord *xlinfo /* to extract blkno info */ ) { |
||||
MemoryContext oldCxt = MemoryContextSwitchTo(insertCtx); |
||||
gistIncompleteInsert *ninsert = (gistIncompleteInsert*)palloc( sizeof(gistIncompleteInsert) ); |
||||
|
||||
ninsert->node = node; |
||||
ninsert->key = key; |
||||
|
||||
if ( lenblk && blkno ) {
|
||||
ninsert->lenblk = lenblk; |
||||
ninsert->blkno = (BlockNumber*)palloc( sizeof(BlockNumber)*ninsert->lenblk ); |
||||
memcpy(ninsert->blkno, blkno, sizeof(BlockNumber)*ninsert->lenblk); |
||||
} else { |
||||
int i; |
||||
|
||||
Assert( xlinfo ); |
||||
ninsert->lenblk = xlinfo->data->npage; |
||||
ninsert->blkno = (BlockNumber*)palloc( sizeof(BlockNumber)*ninsert->lenblk ); |
||||
for(i=0;i<ninsert->lenblk;i++) |
||||
ninsert->blkno[i] = xlinfo->page[i].header->blkno; |
||||
} |
||||
Assert( ninsert->lenblk>0 ); |
||||
|
||||
if ( path && ninsert->pathlen ) { |
||||
ninsert->pathlen = pathlen; |
||||
ninsert->path = (BlockNumber*)palloc( sizeof(BlockNumber)*ninsert->pathlen ); |
||||
memcpy(ninsert->path, path, sizeof(BlockNumber)*ninsert->pathlen); |
||||
} else {
|
||||
ninsert->pathlen = 0; |
||||
ninsert->path = NULL; |
||||
} |
||||
|
||||
incomplete_inserts = lappend(incomplete_inserts, ninsert); |
||||
MemoryContextSwitchTo(oldCxt); |
||||
} |
||||
|
||||
static void |
||||
forgetIncompleteInsert(RelFileNode node, ItemPointerData key) { |
||||
ListCell *l; |
||||
|
||||
foreach(l, incomplete_inserts) { |
||||
gistIncompleteInsert *insert = (gistIncompleteInsert*) lfirst(l); |
||||
|
||||
if ( RelFileNodeEquals(node, insert->node) && ItemPointerEQ( &(insert->key), &(key) ) ) { |
||||
|
||||
/* found */ |
||||
if ( insert->path ) pfree( insert->path ); |
||||
pfree( insert->blkno ); |
||||
incomplete_inserts = list_delete_ptr(incomplete_inserts, insert); |
||||
pfree( insert ); |
||||
break; |
||||
}
|
||||
} |
||||
} |
||||
|
||||
static void |
||||
decodeEntryUpdateRecord(EntryUpdateRecord *decoded, XLogRecord *record) { |
||||
char *begin = XLogRecGetData(record), *ptr; |
||||
int i=0, addpath=0; |
||||
|
||||
decoded->data = (gistxlogEntryUpdate*)begin; |
||||
|
||||
if ( decoded->data->pathlen ) { |
||||
addpath = sizeof(BlockNumber) * decoded->data->pathlen; |
||||
decoded->path = (BlockNumber*)(begin+sizeof( gistxlogEntryUpdate )); |
||||
} else
|
||||
decoded->path = NULL; |
||||
|
||||
decoded->len=0; |
||||
ptr=begin+sizeof( gistxlogEntryUpdate ) + addpath; |
||||
while( ptr - begin < record->xl_len ) { |
||||
decoded->len++; |
||||
ptr += IndexTupleSize( (IndexTuple)ptr ); |
||||
}
|
||||
|
||||
decoded->itup=(IndexTuple*)palloc( sizeof( IndexTuple ) * decoded->len ); |
||||
|
||||
ptr=begin+sizeof( gistxlogEntryUpdate ) + addpath; |
||||
while( ptr - begin < record->xl_len ) { |
||||
decoded->itup[i] = (IndexTuple)ptr; |
||||
ptr += IndexTupleSize( decoded->itup[i] ); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
|
||||
static void |
||||
gistRedoEntryUpdateRecord(XLogRecPtr lsn, XLogRecord *record, bool isnewroot) { |
||||
EntryUpdateRecord xlrec; |
||||
Relation reln; |
||||
Buffer buffer; |
||||
Page page; |
||||
|
||||
decodeEntryUpdateRecord( &xlrec, record ); |
||||
|
||||
reln = XLogOpenRelation(xlrec.data->node); |
||||
if (!RelationIsValid(reln)) |
||||
return; |
||||
buffer = XLogReadBuffer(false, reln, xlrec.data->blkno); |
||||
if (!BufferIsValid(buffer)) |
||||
elog(PANIC, "gistRedoEntryUpdateRecord: block unfound"); |
||||
page = (Page) BufferGetPage(buffer); |
||||
|
||||
if ( isnewroot ) { |
||||
if ( !PageIsNew((PageHeader) page) && XLByteLE(lsn, PageGetLSN(page)) ) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
return; |
||||
} |
||||
} else {
|
||||
if ( PageIsNew((PageHeader) page) ) |
||||
elog(PANIC, "gistRedoEntryUpdateRecord: uninitialized page"); |
||||
if (XLByteLE(lsn, PageGetLSN(page))) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if ( isnewroot ) |
||||
GISTInitBuffer(buffer, 0); |
||||
else if ( xlrec.data->todeleteoffnum != InvalidOffsetNumber )
|
||||
PageIndexTupleDelete(page, xlrec.data->todeleteoffnum); |
||||
|
||||
/* add tuples */ |
||||
if ( xlrec.len > 0 ) { |
||||
OffsetNumber off = (PageIsEmpty(page)) ?
|
||||
FirstOffsetNumber |
||||
: |
||||
OffsetNumberNext(PageGetMaxOffsetNumber(page)); |
||||
|
||||
gistfillbuffer(reln, page, xlrec.itup, xlrec.len, off); |
||||
} |
||||
|
||||
PageSetLSN(page, lsn); |
||||
PageSetTLI(page, ThisTimeLineID); |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
WriteBuffer(buffer); |
||||
|
||||
if ( ItemPointerIsValid( &(xlrec.data->key) ) ) { |
||||
if ( incomplete_inserts != NIL ) |
||||
forgetIncompleteInsert(xlrec.data->node, xlrec.data->key); |
||||
|
||||
if ( !isnewroot && xlrec.data->blkno!=GIST_ROOT_BLKNO ) |
||||
pushIncompleteInsert(xlrec.data->node, xlrec.data->key,
|
||||
&(xlrec.data->blkno), 1, |
||||
xlrec.path, xlrec.data->pathlen, |
||||
NULL); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
decodePageSplitRecord(PageSplitRecord *decoded, XLogRecord *record) { |
||||
char *begin = XLogRecGetData(record), *ptr; |
||||
int i=0, addpath = 0; |
||||
|
||||
decoded->data = (gistxlogPageSplit*)begin; |
||||
decoded->page = (NewPage*)palloc( sizeof(NewPage) * decoded->data->npage ); |
||||
decoded->itup = (IndexTuple*)palloc( sizeof(IndexTuple) * decoded->data->nitup ); |
||||
|
||||
if ( decoded->data->pathlen ) { |
||||
addpath = sizeof(BlockNumber) * decoded->data->pathlen; |
||||
decoded->path = (BlockNumber*)(begin+sizeof( gistxlogEntryUpdate )); |
||||
} else
|
||||
decoded->path = NULL; |
||||
|
||||
ptr=begin+sizeof( gistxlogPageSplit ) + addpath; |
||||
for(i=0;i<decoded->data->nitup;i++) { |
||||
Assert( ptr - begin < record->xl_len ); |
||||
decoded->itup[i] = (IndexTuple)ptr; |
||||
ptr += IndexTupleSize( decoded->itup[i] ); |
||||
} |
||||
|
||||
for(i=0;i<decoded->data->npage;i++) { |
||||
Assert( ptr - begin < record->xl_len ); |
||||
decoded->page[i].header = (gistxlogPage*)ptr; |
||||
ptr += sizeof(gistxlogPage); |
||||
|
||||
Assert( ptr - begin < record->xl_len ); |
||||
decoded->page[i].offnum = (OffsetNumber*)ptr; |
||||
ptr += MAXALIGN( sizeof(OffsetNumber) * decoded->page[i].header->num ); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
gistRedoPageSplitRecord(XLogRecPtr lsn, XLogRecord *record ) { |
||||
PageSplitRecord xlrec; |
||||
Relation reln; |
||||
Buffer buffer; |
||||
Page page; |
||||
int i, len=0; |
||||
IndexTuple *itup, *institup; |
||||
GISTPageOpaque opaque; |
||||
bool release=true; |
||||
|
||||
decodePageSplitRecord( &xlrec, record ); |
||||
|
||||
reln = XLogOpenRelation(xlrec.data->node); |
||||
if (!RelationIsValid(reln)) |
||||
return; |
||||
buffer = XLogReadBuffer( false, reln, xlrec.data->origblkno); |
||||
if (!BufferIsValid(buffer)) |
||||
elog(PANIC, "gistRedoEntryUpdateRecord: block unfound"); |
||||
page = (Page) BufferGetPage(buffer); |
||||
if (PageIsNew((PageHeader) page)) |
||||
elog(PANIC, "gistRedoEntryUpdateRecord: uninitialized page"); |
||||
|
||||
if (XLByteLE(lsn, PageGetLSN(page))) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
return; |
||||
} |
||||
|
||||
if ( xlrec.data->todeleteoffnum != InvalidOffsetNumber ) |
||||
PageIndexTupleDelete(page, xlrec.data->todeleteoffnum); |
||||
|
||||
itup = gistextractbuffer(buffer, &len); |
||||
itup = gistjoinvector(itup, &len, xlrec.itup, xlrec.data->nitup); |
||||
institup = (IndexTuple*)palloc( sizeof(IndexTuple) * len ); |
||||
opaque = (GISTPageOpaque) PageGetSpecialPointer(page); |
||||
|
||||
for(i=0;i<xlrec.data->npage;i++) { |
||||
int j; |
||||
NewPage *newpage = xlrec.page + i;
|
||||
|
||||
/* prepare itup vector */ |
||||
for(j=0;j<newpage->header->num;j++) |
||||
institup[j] = itup[ newpage->offnum[j] - 1 ]; |
||||
|
||||
if ( newpage->header->blkno == xlrec.data->origblkno ) { |
||||
/* IncrBufferRefCount(buffer); */ |
||||
newpage->page = (Page) PageGetTempPage(page, sizeof(GISTPageOpaqueData)); |
||||
newpage->buffer = buffer; |
||||
newpage->is_ok=false;
|
||||
} else { |
||||
newpage->buffer = XLogReadBuffer(true, reln, newpage->header->blkno); |
||||
if (!BufferIsValid(newpage->buffer)) |
||||
elog(PANIC, "gistRedoPageSplitRecord: lost page"); |
||||
newpage->page = (Page) BufferGetPage(newpage->buffer); |
||||
if (!PageIsNew((PageHeader) page) && XLByteLE(lsn, PageGetLSN(newpage->page))) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
newpage->is_ok=true; |
||||
continue; /* good page */ |
||||
} else { |
||||
newpage->is_ok=false; |
||||
GISTInitBuffer(newpage->buffer, opaque->flags & F_LEAF); |
||||
} |
||||
} |
||||
gistfillbuffer(reln, newpage->page, institup, newpage->header->num, FirstOffsetNumber); |
||||
} |
||||
|
||||
for(i=0;i<xlrec.data->npage;i++) { |
||||
NewPage *newpage = xlrec.page + i; |
||||
|
||||
if ( newpage->is_ok ) |
||||
continue; |
||||
|
||||
if ( newpage->header->blkno == xlrec.data->origblkno ) {
|
||||
PageRestoreTempPage(newpage->page, page); |
||||
release = false; |
||||
} |
||||
|
||||
PageSetLSN(newpage->page, lsn); |
||||
PageSetTLI(newpage->page, ThisTimeLineID); |
||||
LockBuffer(newpage->buffer, BUFFER_LOCK_UNLOCK); |
||||
WriteBuffer(newpage->buffer);
|
||||
} |
||||
|
||||
if ( release ) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
} |
||||
|
||||
if ( ItemPointerIsValid( &(xlrec.data->key) ) ) { |
||||
if ( incomplete_inserts != NIL ) |
||||
forgetIncompleteInsert(xlrec.data->node, xlrec.data->key); |
||||
|
||||
pushIncompleteInsert(xlrec.data->node, xlrec.data->key,
|
||||
NULL, 0, |
||||
xlrec.path, xlrec.data->pathlen, |
||||
&xlrec); |
||||
} |
||||
} |
||||
|
||||
static void |
||||
gistRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record) { |
||||
RelFileNode *node = (RelFileNode*)XLogRecGetData(record); |
||||
Relation reln; |
||||
Buffer buffer; |
||||
Page page; |
||||
|
||||
reln = XLogOpenRelation(*node); |
||||
if (!RelationIsValid(reln)) |
||||
return; |
||||
buffer = XLogReadBuffer( true, reln, GIST_ROOT_BLKNO); |
||||
if (!BufferIsValid(buffer)) |
||||
elog(PANIC, "gistRedoCreateIndex: block unfound"); |
||||
page = (Page) BufferGetPage(buffer); |
||||
|
||||
if (!PageIsNew((PageHeader) page) && XLByteLE(lsn, PageGetLSN(page))) { |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
return; |
||||
} |
||||
|
||||
GISTInitBuffer(buffer, F_LEAF); |
||||
|
||||
PageSetLSN(page, lsn); |
||||
PageSetTLI(page, ThisTimeLineID); |
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
WriteBuffer(buffer);
|
||||
} |
||||
|
||||
void |
||||
gist_redo(XLogRecPtr lsn, XLogRecord *record) |
||||
{ |
||||
uint8 info = record->xl_info & ~XLR_INFO_MASK; |
||||
|
||||
MemoryContext oldCxt; |
||||
oldCxt = MemoryContextSwitchTo(opCtx); |
||||
switch (info) { |
||||
case XLOG_GIST_ENTRY_UPDATE: |
||||
case XLOG_GIST_ENTRY_DELETE: |
||||
gistRedoEntryUpdateRecord(lsn, record,false); |
||||
break; |
||||
case XLOG_GIST_NEW_ROOT: |
||||
gistRedoEntryUpdateRecord(lsn, record,true); |
||||
break; |
||||
case XLOG_GIST_PAGE_SPLIT: |
||||
gistRedoPageSplitRecord(lsn, record);
|
||||
break; |
||||
case XLOG_GIST_CREATE_INDEX: |
||||
gistRedoCreateIndex(lsn, record); |
||||
break; |
||||
case XLOG_GIST_INSERT_COMPLETE: |
||||
forgetIncompleteInsert( ((gistxlogInsertComplete*)XLogRecGetData(record))->node,
|
||||
((gistxlogInsertComplete*)XLogRecGetData(record))->key ); |
||||
break; |
||||
default: |
||||
elog(PANIC, "gist_redo: unknown op code %u", info); |
||||
} |
||||
|
||||
MemoryContextSwitchTo(oldCxt); |
||||
MemoryContextReset(opCtx); |
||||
} |
||||
|
||||
static void |
||||
out_target(char *buf, RelFileNode node, ItemPointerData key) |
||||
{ |
||||
sprintf(buf + strlen(buf), "rel %u/%u/%u; tid %u/%u", |
||||
node.spcNode, node.dbNode, node.relNode, |
||||
ItemPointerGetBlockNumber(&key), |
||||
ItemPointerGetOffsetNumber(&key)); |
||||
} |
||||
|
||||
static void |
||||
out_gistxlogEntryUpdate(char *buf, gistxlogEntryUpdate *xlrec) { |
||||
out_target(buf, xlrec->node, xlrec->key); |
||||
sprintf(buf + strlen(buf), "; block number %u; update offset %u;",
|
||||
xlrec->blkno, xlrec->todeleteoffnum); |
||||
} |
||||
|
||||
static void |
||||
out_gistxlogPageSplit(char *buf, gistxlogPageSplit *xlrec) { |
||||
strcat(buf, "page_split: "); |
||||
out_target(buf, xlrec->node, xlrec->key); |
||||
sprintf(buf + strlen(buf), "; block number %u; update offset %u; add %d tuples; split to %d pages",
|
||||
xlrec->origblkno, xlrec->todeleteoffnum, |
||||
xlrec->nitup, xlrec->npage); |
||||
} |
||||
|
||||
void |
||||
gist_desc(char *buf, uint8 xl_info, char *rec) |
||||
{ |
||||
uint8 info = xl_info & ~XLR_INFO_MASK; |
||||
|
||||
switch (info) { |
||||
case XLOG_GIST_ENTRY_UPDATE: |
||||
strcat(buf, "entry_update: "); |
||||
out_gistxlogEntryUpdate(buf, (gistxlogEntryUpdate*)rec); |
||||
break; |
||||
case XLOG_GIST_ENTRY_DELETE: |
||||
strcat(buf, "entry_delete: "); |
||||
out_gistxlogEntryUpdate(buf, (gistxlogEntryUpdate*)rec); |
||||
break; |
||||
case XLOG_GIST_NEW_ROOT: |
||||
strcat(buf, "new_root: "); |
||||
out_target(buf, ((gistxlogEntryUpdate*)rec)->node, ((gistxlogEntryUpdate*)rec)->key); |
||||
break; |
||||
case XLOG_GIST_PAGE_SPLIT: |
||||
out_gistxlogPageSplit(buf, (gistxlogPageSplit*)rec); |
||||
break; |
||||
case XLOG_GIST_CREATE_INDEX: |
||||
sprintf(buf + strlen(buf), "create_index: rel %u/%u/%u",
|
||||
((RelFileNode*)rec)->spcNode,
|
||||
((RelFileNode*)rec)->dbNode,
|
||||
((RelFileNode*)rec)->relNode); |
||||
break; |
||||
case XLOG_GIST_INSERT_COMPLETE: |
||||
strcat(buf, "insert_complete: "); |
||||
out_target(buf, ((gistxlogInsertComplete*)rec)->node, ((gistxlogInsertComplete*)rec)->key);
|
||||
default: |
||||
elog(PANIC, "gist_desc: unknown op code %u", info); |
||||
} |
||||
} |
||||
|
||||
|
||||
#ifdef GIST_INCOMPLETE_INSERT |
||||
static void |
||||
gistContinueInsert(gistIncompleteInsert *insert) { |
||||
GISTSTATE giststate; |
||||
GISTInsertState state; |
||||
int i; |
||||
MemoryContext oldCxt; |
||||
oldCxt = MemoryContextSwitchTo(opCtx); |
||||
|
||||
state.r = XLogOpenRelation(insert->node); |
||||
if (!RelationIsValid(state.r)) |
||||
return; |
||||
|
||||
initGISTstate(&giststate, state.r); |
||||
|
||||
state.needInsertComplete=false; |
||||
ItemPointerSetInvalid( &(state.key) ); |
||||
state.path=NULL; |
||||
state.pathlen=0; |
||||
state.xlog_mode = true; |
||||
|
||||
/* form union tuples */ |
||||
state.itup = (IndexTuple*)palloc(sizeof(IndexTuple)*insert->lenblk); |
||||
state.ituplen = insert->lenblk;
|
||||
for(i=0;i<insert->lenblk;i++) { |
||||
int len=0; |
||||
IndexTuple *itup; |
||||
Buffer buffer; |
||||
Page page; |
||||
|
||||
buffer = XLogReadBuffer(false, state.r, insert->blkno[i]); |
||||
if (!BufferIsValid(buffer)) |
||||
elog(PANIC, "gistContinueInsert: block unfound"); |
||||
page = (Page) BufferGetPage(buffer); |
||||
if ( PageIsNew((PageHeader)page) ) |
||||
elog(PANIC, "gistContinueInsert: uninitialized page"); |
||||
|
||||
itup = gistextractbuffer(buffer, &len); |
||||
state.itup[i] = gistunion(state.r, itup, len, &giststate); |
||||
|
||||
ItemPointerSet( &(state.itup[i]->t_tid), insert->blkno[i], FirstOffsetNumber ); |
||||
|
||||
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); |
||||
ReleaseBuffer(buffer); |
||||
} |
||||
|
||||
if ( insert->pathlen==0 ) {
|
||||
/*it was split root, so we should only make new root*/ |
||||
gistnewroot(state.r, state.itup, state.ituplen, &(state.key), true); |
||||
MemoryContextSwitchTo(oldCxt); |
||||
MemoryContextReset(opCtx); |
||||
return; |
||||
} |
||||
|
||||
/* form stack */ |
||||
state.stack=NULL; |
||||
for(i=0;i<insert->pathlen;i++) { |
||||
int j,len=0; |
||||
IndexTuple *itup; |
||||
GISTInsertStack *top = (GISTInsertStack*)palloc( sizeof(GISTInsertStack) ); |
||||
|
||||
top->blkno = insert->path[i]; |
||||
top->buffer = XLogReadBuffer(false, state.r, top->blkno); |
||||
if (!BufferIsValid(top->buffer)) |
||||
elog(PANIC, "gistContinueInsert: block unfound"); |
||||
top->page = (Page) BufferGetPage(top->buffer); |
||||
if ( PageIsNew((PageHeader)(top->page)) ) |
||||
elog(PANIC, "gistContinueInsert: uninitialized page"); |
||||
|
||||
top->todelete = false;
|
||||
|
||||
/* find childoffnum */ |
||||
itup = gistextractbuffer(top->buffer, &len); |
||||
top->childoffnum=InvalidOffsetNumber; |
||||
for(j=0;j<len && top->childoffnum==InvalidOffsetNumber;j++) { |
||||
BlockNumber blkno = ItemPointerGetBlockNumber( &(itup[j]->t_tid) );
|
||||
|
||||
if ( i==0 ) { |
||||
int k;
|
||||
for(k=0;k<insert->lenblk;k++) |
||||
if ( insert->blkno[k] == blkno ) { |
||||
top->childoffnum = j+1; |
||||
break; |
||||
} |
||||
} else if ( insert->path[i-1]==blkno ) |
||||
top->childoffnum = j+1; |
||||
} |
||||
|
||||
if ( top->childoffnum==InvalidOffsetNumber ) { |
||||
elog(WARNING, "gistContinueInsert: unknown parent, REINDEX GiST Indexes"); |
||||
return; |
||||
} |
||||
|
||||
if ( i==0 )
|
||||
PageIndexTupleDelete(top->page, top->childoffnum); |
||||
|
||||
/* install item on right place in stack */ |
||||
top->parent=NULL; |
||||
if ( state.stack ) { |
||||
GISTInsertStack *ptr = state.stack; |
||||
while( ptr->parent ) |
||||
ptr = ptr->parent; |
||||
ptr->parent=top; |
||||
} else |
||||
state.stack = top; |
||||
} |
||||
|
||||
/* Good. Now we can continue insert */ |
||||
|
||||
gistmakedeal(&state, &giststate); |
||||
|
||||
MemoryContextSwitchTo(oldCxt); |
||||
MemoryContextReset(opCtx); |
||||
} |
||||
#endif |
||||
|
||||
void |
||||
gist_xlog_startup(void) { |
||||
incomplete_inserts=NIL; |
||||
insertCtx = AllocSetContextCreate(CurrentMemoryContext, |
||||
"GiST insert in xlog temporary context",
|
||||
ALLOCSET_DEFAULT_MINSIZE, |
||||
ALLOCSET_DEFAULT_INITSIZE, |
||||
ALLOCSET_DEFAULT_MAXSIZE); |
||||
opCtx = createTempGistContext(); |
||||
} |
||||
|
||||
void |
||||
gist_xlog_cleanup(void) { |
||||
ListCell *l; |
||||
|
||||
foreach(l, incomplete_inserts) { |
||||
gistIncompleteInsert *insert = (gistIncompleteInsert*) lfirst(l); |
||||
char buf[1024]; |
||||
|
||||
*buf='\0'; |
||||
out_target(buf, insert->node, insert->key); |
||||
elog(LOG,"Incomplete insert: %s; It's needed to reindex", buf); |
||||
#ifdef GIST_INCOMPLETE_INSERT |
||||
gistContinueInsert(insert); |
||||
#endif |
||||
} |
||||
MemoryContextDelete(opCtx); |
||||
MemoryContextDelete(insertCtx);
|
||||
} |
||||
|
||||
Loading…
Reference in new issue