1 /*-------------------------------------------------------------------------
7 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
17 #include "access/gist_private.h"
18 #include "utils/rel.h"
24 OffsetNumber
*entries
;
31 * Forms unions of subkeys after page split, but
32 * uses only tuples aren't in groups of equalent tuples
35 gistunionsubkeyvec(GISTSTATE
*giststate
, IndexTuple
*itvec
,
36 GistSplitUnion
*gsvp
, int startkey
)
38 IndexTuple
*cleanedItVec
;
42 cleanedItVec
= (IndexTuple
*) palloc(sizeof(IndexTuple
) * gsvp
->len
);
44 for (i
= 0; i
< gsvp
->len
; i
++)
46 if (gsvp
->equiv
&& gsvp
->equiv
[gsvp
->entries
[i
]])
49 cleanedItVec
[cleanedLen
++] = itvec
[gsvp
->entries
[i
] - 1];
52 gistMakeUnionItVec(giststate
, cleanedItVec
, cleanedLen
, startkey
,
53 gsvp
->attr
, gsvp
->isnull
);
59 * unions subkeys for after user picksplit over attno-1 column
62 gistunionsubkey(GISTSTATE
*giststate
, IndexTuple
*itvec
, GistSplitVector
*spl
, int attno
)
66 gsvp
.equiv
= spl
->spl_equiv
;
68 gsvp
.attr
= spl
->spl_lattr
;
69 gsvp
.len
= spl
->splitVector
.spl_nleft
;
70 gsvp
.entries
= spl
->splitVector
.spl_left
;
71 gsvp
.isnull
= spl
->spl_lisnull
;
73 gistunionsubkeyvec(giststate
, itvec
, &gsvp
, attno
);
75 gsvp
.attr
= spl
->spl_rattr
;
76 gsvp
.len
= spl
->splitVector
.spl_nright
;
77 gsvp
.entries
= spl
->splitVector
.spl_right
;
78 gsvp
.isnull
= spl
->spl_risnull
;
80 gistunionsubkeyvec(giststate
, itvec
, &gsvp
, attno
);
84 * find group in vector with equivalent value
87 gistfindgroup(Relation r
, GISTSTATE
*giststate
, GISTENTRY
*valvec
, GistSplitVector
*spl
, int attno
)
94 * attno key is always not null (see gistSplitByKey), so we may not check
97 gistentryinit(entry
, spl
->splitVector
.spl_rdatum
, r
, NULL
, (OffsetNumber
) 0, FALSE
);
98 for (i
= 0; i
< spl
->splitVector
.spl_nleft
; i
++)
100 float penalty
= gistpenalty(giststate
, attno
, &entry
, false,
101 &valvec
[spl
->splitVector
.spl_left
[i
]], false);
105 spl
->spl_equiv
[spl
->splitVector
.spl_left
[i
]] = true;
110 gistentryinit(entry
, spl
->splitVector
.spl_ldatum
, r
, NULL
, (OffsetNumber
) 0, FALSE
);
111 for (i
= 0; i
< spl
->splitVector
.spl_nright
; i
++)
113 float penalty
= gistpenalty(giststate
, attno
, &entry
, false,
114 &valvec
[spl
->splitVector
.spl_right
[i
]], false);
118 spl
->spl_equiv
[spl
->splitVector
.spl_right
[i
]] = true;
127 cleanupOffsets(OffsetNumber
*a
, int *len
, bool *equiv
, int *LenEquiv
)
131 OffsetNumber
*curwpos
;
135 for (i
= 0; i
< *len
; i
++)
137 if (equiv
[a
[i
]] == FALSE
)
144 /* corner case: we shouldn't make void array */
147 equiv
[a
[i
]] = FALSE
; /* mark item as non-equivalent */
148 i
--; /* redo the same */
161 placeOne(Relation r
, GISTSTATE
*giststate
, GistSplitVector
*v
, IndexTuple itup
, OffsetNumber off
, int attno
)
163 GISTENTRY identry
[INDEX_MAX_KEYS
];
164 bool isnull
[INDEX_MAX_KEYS
];
167 gistDeCompressAtt(giststate
, r
, itup
, NULL
, (OffsetNumber
) 0, identry
, isnull
);
169 for (; attno
< giststate
->tupdesc
->natts
; attno
++)
175 gistentryinit(entry
, v
->spl_lattr
[attno
], r
, NULL
, 0, FALSE
);
176 lpenalty
= gistpenalty(giststate
, attno
, &entry
, v
->spl_lisnull
[attno
], identry
+ attno
, isnull
[attno
]);
177 gistentryinit(entry
, v
->spl_rattr
[attno
], r
, NULL
, 0, FALSE
);
178 rpenalty
= gistpenalty(giststate
, attno
, &entry
, v
->spl_risnull
[attno
], identry
+ attno
, isnull
[attno
]);
180 if (lpenalty
!= rpenalty
)
182 if (lpenalty
> rpenalty
)
189 v
->splitVector
.spl_left
[v
->splitVector
.spl_nleft
++] = off
;
191 v
->splitVector
.spl_right
[v
->splitVector
.spl_nright
++] = off
;
194 #define SWAPVAR( s, d, t ) \
202 * adjust left and right unions according to splits by previous
203 * split by firsts columns. This function is called only in case
204 * when pickSplit doesn't support subspplit.
208 supportSecondarySplit(Relation r
, GISTSTATE
*giststate
, int attno
, GIST_SPLITVEC
*sv
, Datum oldL
, Datum oldR
)
210 bool leaveOnLeft
= true,
217 gistentryinit(entryL
, oldL
, r
, NULL
, 0, FALSE
);
218 gistentryinit(entryR
, oldR
, r
, NULL
, 0, FALSE
);
219 gistentryinit(entrySL
, sv
->spl_ldatum
, r
, NULL
, 0, FALSE
);
220 gistentryinit(entrySR
, sv
->spl_rdatum
, r
, NULL
, 0, FALSE
);
222 if (sv
->spl_ldatum_exists
&& sv
->spl_rdatum_exists
)
227 penalty1
= gistpenalty(giststate
, attno
, &entryL
, false, &entrySL
, false) +
228 gistpenalty(giststate
, attno
, &entryR
, false, &entrySR
, false);
229 penalty2
= gistpenalty(giststate
, attno
, &entryL
, false, &entrySR
, false) +
230 gistpenalty(giststate
, attno
, &entryR
, false, &entrySL
, false);
232 if (penalty1
> penalty2
)
238 GISTENTRY
*entry1
= (sv
->spl_ldatum_exists
) ? &entryL
: &entryR
;
243 * there is only one previously defined union, so we just choose swap
244 * or not by lowest penalty
247 penalty1
= gistpenalty(giststate
, attno
, entry1
, false, &entrySL
, false);
248 penalty2
= gistpenalty(giststate
, attno
, entry1
, false, &entrySR
, false);
250 if (penalty1
< penalty2
)
251 leaveOnLeft
= (sv
->spl_ldatum_exists
) ? true : false;
253 leaveOnLeft
= (sv
->spl_rdatum_exists
) ? true : false;
256 if (leaveOnLeft
== false)
259 * swap left and right
265 SWAPVAR(sv
->spl_left
, sv
->spl_right
, off
);
266 SWAPVAR(sv
->spl_nleft
, sv
->spl_nright
, noff
);
267 SWAPVAR(sv
->spl_ldatum
, sv
->spl_rdatum
, datum
);
268 gistentryinit(entrySL
, sv
->spl_ldatum
, r
, NULL
, 0, FALSE
);
269 gistentryinit(entrySR
, sv
->spl_rdatum
, r
, NULL
, 0, FALSE
);
272 if (sv
->spl_ldatum_exists
)
273 gistMakeUnionKey(giststate
, attno
, &entryL
, false, &entrySL
, false,
274 &sv
->spl_ldatum
, &tmpBool
);
276 if (sv
->spl_rdatum_exists
)
277 gistMakeUnionKey(giststate
, attno
, &entryR
, false, &entrySR
, false,
278 &sv
->spl_rdatum
, &tmpBool
);
280 sv
->spl_ldatum_exists
= sv
->spl_rdatum_exists
= false;
284 * Calls user picksplit method for attno columns to split vector to
285 * two vectors. May use attno+n columns data to
287 * Returns TRUE and v->spl_equiv = NULL if left and right unions of attno columns are the same,
288 * so caller may find better split
289 * Returns TRUE and v->spl_equiv != NULL if there is tuples which may be freely moved
292 gistUserPicksplit(Relation r
, GistEntryVector
*entryvec
, int attno
, GistSplitVector
*v
,
293 IndexTuple
*itup
, int len
, GISTSTATE
*giststate
)
295 GIST_SPLITVEC
*sv
= &v
->splitVector
;
298 * now let the user-defined picksplit function set up the split vector; in
299 * entryvec have no null value!!
302 sv
->spl_ldatum_exists
= (v
->spl_lisnull
[attno
]) ? false : true;
303 sv
->spl_rdatum_exists
= (v
->spl_risnull
[attno
]) ? false : true;
304 sv
->spl_ldatum
= v
->spl_lattr
[attno
];
305 sv
->spl_rdatum
= v
->spl_rattr
[attno
];
307 FunctionCall2(&giststate
->picksplitFn
[attno
],
308 PointerGetDatum(entryvec
),
309 PointerGetDatum(sv
));
311 /* compatibility with old code */
312 if (sv
->spl_left
[sv
->spl_nleft
- 1] == InvalidOffsetNumber
)
313 sv
->spl_left
[sv
->spl_nleft
- 1] = (OffsetNumber
) (entryvec
->n
- 1);
314 if (sv
->spl_right
[sv
->spl_nright
- 1] == InvalidOffsetNumber
)
315 sv
->spl_right
[sv
->spl_nright
- 1] = (OffsetNumber
) (entryvec
->n
- 1);
317 if (sv
->spl_ldatum_exists
|| sv
->spl_rdatum_exists
)
319 elog(LOG
, "PickSplit method of %d columns of index '%s' doesn't support secondary split",
320 attno
+ 1, RelationGetRelationName(r
));
322 supportSecondarySplit(r
, giststate
, attno
, sv
, v
->spl_lattr
[attno
], v
->spl_rattr
[attno
]);
325 v
->spl_lattr
[attno
] = sv
->spl_ldatum
;
326 v
->spl_rattr
[attno
] = sv
->spl_rdatum
;
327 v
->spl_lisnull
[attno
] = false;
328 v
->spl_risnull
[attno
] = false;
331 * if index is multikey, then we must to try get smaller bounding box for
336 if (giststate
->tupdesc
->natts
> 1 && attno
+ 1 != giststate
->tupdesc
->natts
)
338 if (gistKeyIsEQ(giststate
, attno
, sv
->spl_ldatum
, sv
->spl_rdatum
))
341 * Left and right key's unions are equial, so we can get better
342 * split by following columns. Note, unions for attno columns are
352 v
->spl_equiv
= (bool *) palloc0(sizeof(bool) * (entryvec
->n
+ 1));
354 LenEquiv
= gistfindgroup(r
, giststate
, entryvec
->vector
, v
, attno
);
357 * if possible, we should distribute equivalent tuples
361 gistunionsubkey(giststate
, itup
, v
, attno
+ 1);
365 cleanupOffsets(sv
->spl_left
, &sv
->spl_nleft
, v
->spl_equiv
, &LenEquiv
);
366 cleanupOffsets(sv
->spl_right
, &sv
->spl_nright
, v
->spl_equiv
, &LenEquiv
);
368 gistunionsubkey(giststate
, itup
, v
, attno
+ 1);
372 * In case with one tuple we just choose left-right by
373 * penalty. It's simplify user-defined pickSplit
375 OffsetNumber toMove
= InvalidOffsetNumber
;
377 for (toMove
= FirstOffsetNumber
; toMove
< entryvec
->n
; toMove
++)
378 if (v
->spl_equiv
[toMove
])
380 Assert(toMove
< entryvec
->n
);
382 placeOne(r
, giststate
, v
, itup
[toMove
- 1], toMove
, attno
+ 1);
385 * redo gistunionsubkey(): it will not degradate
386 * performance, because it's very rarely
389 gistunionsubkey(giststate
, itup
, v
, attno
+ 1);
393 else if (LenEquiv
> 1)
406 gistSplitHalf(GIST_SPLITVEC
*v
, int len
)
410 v
->spl_nright
= v
->spl_nleft
= 0;
411 v
->spl_left
= (OffsetNumber
*) palloc(len
* sizeof(OffsetNumber
));
412 v
->spl_right
= (OffsetNumber
*) palloc(len
* sizeof(OffsetNumber
));
413 for (i
= 1; i
<= len
; i
++)
415 v
->spl_right
[v
->spl_nright
++] = i
;
417 v
->spl_left
[v
->spl_nleft
++] = i
;
421 * if it was invalid tuple then we need special processing.
422 * We move all invalid tuples on right page.
424 * if there is no place on left page, gistSplit will be called one more
425 * time for left page.
427 * Normally, we never exec this code, but after crash replay it's possible
428 * to get 'invalid' tuples (probability is low enough)
431 gistSplitByInvalid(GISTSTATE
*giststate
, GistSplitVector
*v
, IndexTuple
*itup
, int len
)
434 static OffsetNumber offInvTuples
[MaxOffsetNumber
];
435 int nOffInvTuples
= 0;
437 for (i
= 1; i
<= len
; i
++)
438 if (GistTupleIsInvalid(itup
[i
- 1]))
439 offInvTuples
[nOffInvTuples
++] = i
;
441 if (nOffInvTuples
== len
)
443 /* corner case, all tuples are invalid */
444 v
->spl_rightvalid
= v
->spl_leftvalid
= false;
445 gistSplitHalf(&v
->splitVector
, len
);
451 v
->splitVector
.spl_right
= offInvTuples
;
452 v
->splitVector
.spl_nright
= nOffInvTuples
;
453 v
->spl_rightvalid
= false;
455 v
->splitVector
.spl_left
= (OffsetNumber
*) palloc(len
* sizeof(OffsetNumber
));
456 v
->splitVector
.spl_nleft
= 0;
457 for (i
= 1; i
<= len
; i
++)
458 if (!GistTupleIsInvalid(itup
[i
- 1]))
459 v
->splitVector
.spl_left
[v
->splitVector
.spl_nleft
++] = i
;
460 v
->spl_leftvalid
= true;
463 gsvp
.attr
= v
->spl_lattr
;
464 gsvp
.len
= v
->splitVector
.spl_nleft
;
465 gsvp
.entries
= v
->splitVector
.spl_left
;
466 gsvp
.isnull
= v
->spl_lisnull
;
468 gistunionsubkeyvec(giststate
, itup
, &gsvp
, 0);
473 * trys to split page by attno key, in a case of null
474 * values move its to separate page.
477 gistSplitByKey(Relation r
, Page page
, IndexTuple
*itup
, int len
, GISTSTATE
*giststate
,
478 GistSplitVector
*v
, GistEntryVector
*entryvec
, int attno
)
481 static OffsetNumber offNullTuples
[MaxOffsetNumber
];
482 int nOffNullTuples
= 0;
484 for (i
= 1; i
<= len
; i
++)
489 if (!GistPageIsLeaf(page
) && GistTupleIsInvalid(itup
[i
- 1]))
491 gistSplitByInvalid(giststate
, v
, itup
, len
);
495 datum
= index_getattr(itup
[i
- 1], attno
+ 1, giststate
->tupdesc
, &IsNull
);
496 gistdentryinit(giststate
, attno
, &(entryvec
->vector
[i
]),
500 offNullTuples
[nOffNullTuples
++] = i
;
503 v
->spl_leftvalid
= v
->spl_rightvalid
= true;
505 if (nOffNullTuples
== len
)
508 * Corner case: All keys in attno column are null, we should try to
509 * split by keys in next column. It all keys in all columns are NULL
510 * just split page half by half
512 v
->spl_risnull
[attno
] = v
->spl_lisnull
[attno
] = TRUE
;
514 if (attno
+ 1 == r
->rd_att
->natts
)
515 gistSplitHalf(&v
->splitVector
, len
);
517 gistSplitByKey(r
, page
, itup
, len
, giststate
, v
, entryvec
, attno
+ 1);
519 else if (nOffNullTuples
> 0)
524 * We don't want to mix NULLs and not-NULLs keys on one page, so move
525 * nulls to right page
527 v
->splitVector
.spl_right
= offNullTuples
;
528 v
->splitVector
.spl_nright
= nOffNullTuples
;
529 v
->spl_risnull
[attno
] = TRUE
;
531 v
->splitVector
.spl_left
= (OffsetNumber
*) palloc(len
* sizeof(OffsetNumber
));
532 v
->splitVector
.spl_nleft
= 0;
533 for (i
= 1; i
<= len
; i
++)
534 if (j
< v
->splitVector
.spl_nright
&& offNullTuples
[j
] == i
)
537 v
->splitVector
.spl_left
[v
->splitVector
.spl_nleft
++] = i
;
540 gistunionsubkey(giststate
, itup
, v
, attno
);
545 * all keys are not-null
547 entryvec
->n
= len
+ 1;
549 if (gistUserPicksplit(r
, entryvec
, attno
, v
, itup
, len
, giststate
) && attno
+ 1 != r
->rd_att
->natts
)
552 * Splitting on attno column is not optimized: there is a tuples
553 * which can be freely left or right page, we will try to split
554 * page by following columns
556 if (v
->spl_equiv
== NULL
)
559 * simple case: left and right keys for attno column are
562 gistSplitByKey(r
, page
, itup
, len
, giststate
, v
, entryvec
, attno
+ 1);
566 /* we should clean up vector from already distributed tuples */
567 IndexTuple
*newitup
= (IndexTuple
*) palloc((len
+ 1) * sizeof(IndexTuple
));
568 OffsetNumber
*map
= (OffsetNumber
*) palloc((len
+ 1) * sizeof(IndexTuple
));
570 GIST_SPLITVEC backupSplit
= v
->splitVector
;
572 for (i
= 0; i
< len
; i
++)
573 if (v
->spl_equiv
[i
+ 1])
576 newitup
[newlen
++] = itup
[i
];
581 backupSplit
.spl_left
= (OffsetNumber
*) palloc(sizeof(OffsetNumber
) * len
);
582 memcpy(backupSplit
.spl_left
, v
->splitVector
.spl_left
, sizeof(OffsetNumber
) * v
->splitVector
.spl_nleft
);
583 backupSplit
.spl_right
= (OffsetNumber
*) palloc(sizeof(OffsetNumber
) * len
);
584 memcpy(backupSplit
.spl_right
, v
->splitVector
.spl_right
, sizeof(OffsetNumber
) * v
->splitVector
.spl_nright
);
586 gistSplitByKey(r
, page
, newitup
, newlen
, giststate
, v
, entryvec
, attno
+ 1);
588 /* merge result of subsplit */
589 for (i
= 0; i
< v
->splitVector
.spl_nleft
; i
++)
590 backupSplit
.spl_left
[backupSplit
.spl_nleft
++] = map
[v
->splitVector
.spl_left
[i
] - 1];
591 for (i
= 0; i
< v
->splitVector
.spl_nright
; i
++)
592 backupSplit
.spl_right
[backupSplit
.spl_nright
++] = map
[v
->splitVector
.spl_right
[i
] - 1];
594 v
->splitVector
= backupSplit
;
595 /* reunion left and right datums */
596 gistunionsubkey(giststate
, itup
, v
, attno
);