Overview
Note: This feature is currently a work in progress. Index building and maintenance are partially implemented. Currently in branch: SPR-1036-GiSTGiST (Generalized Search Tree) support enables efficient indexing for geometric data types, range types, and other complex data structures. GiST is a balanced tree structure that allows custom operator classes to define how data is organized and searched within the index. The implementation uses PostgreSQL’s GiST opclass methods (compress, penalty, union, picksplit, consistent) to build and maintain a tree structure where:
- Leaf nodes store the actual indexed values and references to table rows
- Internal (branch) nodes store predicates (bounding boxes, ranges, etc.) that cover all entries in their subtrees
gist_point_ops for geometric point data.
Key Components
Index Building
| Component | Location | Purpose |
|---|---|---|
| GiST Helpers | src/storage/gist_helpers.cc | Opclass method invocation (compress, penalty, union, picksplit) |
| GiST Schema Builder | src/sys_tbl_mgr/schema_helpers.cc | Creates GiST index schema |
| GiST Index Root | src/sys_tbl_mgr/mutable_table.cc | Initializes MutableBTree configured for GiST storage |
| Insertion Logic | src/storage/mutable_btree.cc | Penalty-based subtree selection and insertion |
| Cache Layer | src/storage/cache.cc | GiST-specific page insertion with subtree selection |
GiST Index Schema
GiST indexes use the same schema structure as regular B-tree indexes:| Field | Type | Description |
|---|---|---|
| Indexed columns | Various | One or more columns with GiST-compatible types |
__springtail_internal_row_id | UINT64 | Reference to the source row |
- Predicate/Union keys: Compressed representations (e.g., bounding boxes) covering all child entries
- Child page ID: Reference to the child page
GiST Data Structure
Supported Opclass Methods
| Method | Support Number | Purpose |
|---|---|---|
GIST_CONSISTENT | 1 | Determine if entry satisfies query predicate |
GIST_UNION | 2 | Compute union/bounding predicate of entries |
GIST_COMPRESS | 3 | Convert leaf value to compressed index representation |
GIST_DECOMPRESS | 4 | Convert index representation back to original form |
GIST_PENALTY | 5 | Compute penalty for inserting entry into subtree |
GIST_PICKSPLIT | 6 | Split overflowing page into two |
GIST_EQUAL | 7 | Check equality of keys |
GIST_DISTANCE | 8 | Compute distance for KNN searches |
Data Flow
Index Building
Index Insertion
Incremental Maintenance
Implementation
GistEntry Extraction
Converts table tuple to compressed GiST index entry:Penalty Computation
Determines best subtree for insertion using opclass penalty method:Subtree Selection
Chooses optimal child page for insertion based on penalty:Union Computation (for Internal Nodes)
Computes bounding predicate covering all child entries:Schema Creation
GiST index schema currently delegates to standard index schema:MutableBTree Insertion
Path to Completion
The following work remains to complete GiST index support:1. Index Building - Full initial index build
- Implement
Indexer::_build_gist_index()to iterate table and build initial tree - Handle tree construction with proper internal node creation using UNION
- Implement reconciliation logic for mutations after initial build XID
2. Page Splitting - Handle overflow during insertion
- Implement
compute_picksplit()to split overflowing pages - Currently commented out in
gist_helpers.cc - Use
GIST_PICKSPLITopclass method to partition entries - Update parent nodes with new predicates after split
- Maintain tree balance
3. Tree Navigation - Complete subtree selection
- Current implementation only handles single-level selection
- Need recursive descent for multi-level trees
- Proper leaf node detection and insertion
4. Index Scanning - Query execution using GiST index
- Implement
GIST_CONSISTENTmethod invocation for query predicates - Create GiST iterator for index scans
- Support various search operators (
<@,&&,~, etc.) - Implement KNN search using
GIST_DISTANCEmethod
5. Deletion and Maintenance
- Implement entry removal from GiST tree
- Handle internal node updates when children change
- Tree rebalancing and page merging
- Vacuum support for GiST indexes
6. Internal Node Management
- Proper storage and retrieval of internal node predicates
- Union computation during page splits and merges
- Predicate updates when child pages change
7. Opclass Validation
- Implement validation in
Server::_check_gist_index_columns() - Similar to GIN’s
ALLOWED_GIN_OPSlist - Verify required methods are available for each opclass
8. Testing
- End-to-end testing with
gist_point_opsand geometric queries - Test files exist in
python/testing/proxy/tests/sql/gist.sql - Validate tree structure and correctness
- Performance testing with large datasets
Current Status
Implemented:- ✅ GistEntry structure and helper functions
- ✅ Opclass method invocation framework (compress, penalty, union)
- ✅ Basic insertion path with penalty-based subtree selection
- ✅ Schema creation infrastructure
- ✅ Field ↔ Datum conversion utilities
- ⚠️ Page insertion (single-level only, no splits)
- ⚠️ Union computation (implemented but not used in tree building)
- ❌ Full index building (Indexer integration)
- ❌ Page splitting (picksplit)
- ❌ Multi-level tree navigation
- ❌ Index scanning and query execution
- ❌ Deletion and maintenance
- ❌ Internal node management
Design Notes
Differences from B-tree Indexes
Unlike B-tree indexes which store (key, value) pairs:- Leaf nodes store compressed representations of indexed values
- Internal nodes store predicates (unions/bounds) rather than actual values
- Insertion uses penalty-based selection rather than key comparison
- Splitting uses custom picksplit logic rather than median split
Opclass Integration
GiST heavily relies on PostgreSQL opclass methods:PgExtnRegistryprovides access to extension-defined opclass methods- Each indexed column can have a different opclass
- Methods operate on
Datumvalues (PostgreSQL’s internal representation) - Conversion between Springtail’s
Fieldtypes andDatumis crucial
Storage Considerations
- Leaf entries: Store compressed keys + internal_row_id
- Branch entries: Store union predicates + child page ID
- Current schema treats both uniformly; may need distinction for internal nodes
- Extent-based storage may need adaptation for variable-sized predicates