GiST stands for Generalized Search Tree. It was introduced in seminal paper "Generalized Search Trees for Database Systems", 1995,Joseph M. Hellerstein,Jeffrey F. Naughton,Avi Pfeffer (PS, 390Kb) and implemented by J. Hellerstein and P.Aoki in an early version of PostgreSQL ( more details is available from The GiST Indexing Project at Berkeley). As an "university" project it had a limited number of features and was in rare use. Since 7.1 version of PostgreSQL the GiST was taken up by Oleg Bartunov and Teodor Sigaev, see GiST development for information about current development.
Current implementation of GiST supports:
GiST is used in several public projects, like PosGIS project, which adds support for geographic objects to the PostgreSQL, OpenFTS - Open Source Full Text Search engine, which provides online indexing of data and relevance ranking for database searching. Check list of popular GiST based extensions.
This document describes algorithms implemented in GiST PostgreSQL v.8.1X
Source files are located under src/backend/access/gist and header files - under src/include/access in PostgreSQL source tree.
Algoritms implemented in PostgreSQL were developed following paper "Access Methods for Next-Generation Database Systems" by Marcel Kornaker (PDF.gz).
The original algorithms were modified by following reasons:
Because of the above reasons, we have to revised interaction of GiST CORE and PostgreSQL WAL system. Moreover, we encountered (and solved) a problem of uncompleted insertions when recovering after crash, which was not touched in the paper.
Function gettuple finds a tuple, which satisfies the search predicate. It store their state and returns next tuple under subsequent calls. Stack contains page, its LSN and LSN of parent page and currentposition is saved between calls.
gettuple(search-pred) if ( firsttime ) push(stack, [root, 0, 0]) // page, LSN, parentLSN currentposition=0 end top = top of stack while(true) latch( top->page, S-mode ) if ( top->page->lsn != top->lsn ) top->lsn = top->page->lsn currentposition=0 if ( top->parentlsn < top->page->nsn ) add to stack rightlink else currentposition++ end while(true) currentposition = find_first_match( currentposition ) if ( currentposition is invalid ) unlatch( top->page ) pop stack top = top of stack break loop else if ( top->page is leaf ) unlatch( top->page ) return tuple else add to stack child page end currentposition++ end end
INSERT guarantees that the GiST tree remains balanced. User defined key method Penalty is used for choosing a subtree to insert; method PickSplit is used for the node splitting algorithm; method Union is used for propagating changes upward to maintain the tree properties.
NOTICE: We modified original INSERT algorithm for perfomance reason. In particularly, it's now single-pass algorithm.
Function findLeaf is used to identify subtree for insertion. Page, in which insertion is proceeded, is locked as well as its parent page. Functions findParent and findPath are used to find parent pages, which could be changed because of concurrent access. Function pageSplit is reccurrent and could split page by more than 2 pages, which could be necessary if keys have different lengths or more than one key are inserted (in such situation, user defined function pickSplit cannot guarantee free space on page).
findLeaf(new-key) push(stack, [root, 0]) //page, LSN while(true) top = top of stack latch( top->page, S-mode ) top->lsn = top->page->lsn if ( top->parent->lsn < top->page->nsn ) unlatch( top->page ) pop stack else if ( top->page is not leaf ) push( stack, [get_best_child(top->page, new-key), 0] ) unlatch( top->page ) else unlatch( top->page ) latch( top->page, X-mode ) if ( top->page is not leaf ) unlatch( top->page ) pop stack else if ( top->parent->lsn < top->page->nsn ) unlatch( top->page ) pop stack else break loop end end end findPath( stack item ) push stack, [root, 0, 0] // page, LSN, parent while( stack ) top = top of stack latch( top->page, S-mode ) if ( top->parent->page->lsn < top->page->nsn ) push stack, [ top->page->rightlink, 0, top->parent ] end for( each tuple on page ) if ( tuple->pagepointer == item->page ) break loop else add to stack at the end [tuple->pagepointer,0, top] end end unlatch( top->page ) pop stack end findParent( stack item ) parent = item->parent latch( parent->page, X-mode ) if ( parent->page->lsn != parent->lsn ) while(true) search parent tuple on parent->page, if found the return rightlink = parent->page->rightlink unlatch( parent->page ) if ( rightlink is incorrect ) break loop end parent->page = rightlink latch( parent->page, X-mode ) end findPath( item->parent ) replace part of stack to new one findParent( item ) end pageSplit(page, allkeys) (lkeys, rkeys) = pickSplit( allkeys ) if ( page is root ) lpage = new page else lpage = page rpage = new page if ( no space left on rpage ) newkeys = pageSplit( rpage, rkeys ) else push newkeys, union(rkeys) end if ( no space left on lpage ) push newkeys, pageSplit( lpage, lkeys ) else push newkeys, union(lkeys) end return newkeys placetopage(page, keysarray) if ( no space left on page ) keysarray = pageSplit(page, [ extract_keys(page), keysarray]) last page in chain gets old NSN, original and others - new NSN from current LSN if ( page is root ) make new root with keysarray end else put keysarray on page if ( length of keysarray > 1 ) keysarray = [ union(keysarray) ] end end insert(new-key) findLeaf(new-key) keysarray = [new-key] top = top of stack while(true) findParent( top ) keysarray = placetopage(top->page, keysarray) unlatch( top->page ) pop stack; top = top of stack if (length of keysarray == 1) newboundingkey = union(oldboundingkey, keysarray) if (newboundingkey == oldboundingkey) unlatch top->page break loop end end end