-
Notifications
You must be signed in to change notification settings - Fork 0
Operator class support
This document describes the implementation of operator class (opclass) support in Springtail, enabling the system to handle GIN and GiST secondary indexes in addition to the existing B-tree indexes.
Status: This feature is currently in development on branch
SPR-1090-gin-gist-base-3and has not been merged tomain.
PostgreSQL uses operator classes to define the behavior of indexes for different data types. Each index type (B-tree, GIN, GiST) requires specific support functions identified by support numbers. For example:
-
GIN indexes use functions like
extractValue,extractQuery, andconsistent -
GiST indexes use functions like
consistent,union,compress,decompress, andpenalty
Previously, Springtail only supported B-tree secondary indexes. This implementation extends the system to:
- Capture and store operator class metadata from PostgreSQL
- Route index operations to the appropriate opclass-specific functions
- Provide the foundation for building and maintaining GIN/GiST indexes
- Store
opclass(operator class name) for each index column andindex_type(btree, gin, gist) for each index - Enable dynamic invocation of opclass support functions via
OpClassHandler - Prepare the indexer infrastructure to handle non-B-tree index types
struct OpClassHandler {
using OpClassFunc = uintptr_t (*)(const std::string& opclass_name,
int support_number,
uintptr_t /*Datum*/ datum);
OpClassFunc opclass_func = nullptr;
ExtensionContext context = {};
};This handler encapsulates a callback for invoking opclass-specific functions. The support_number parameter identifies which support function to call (e.g., GIST_CONSISTENT = 1, GIN_COMPARE = 1).
static constexpr std::string_view INDEX_TYPE_GIN = "gin";
static constexpr std::string_view INDEX_TYPE_GIST = "gist";
static constexpr std::string_view INDEX_TYPE_BTREE = "btree";Extended PgMsgSchemaIndexColumn with:
std::string opclass; // operator class name (e.g., "tsvector_ops", "int4_ops")Extended PgMsgIndex with:
std::string index_type; // "gin", "gist", or "btree"Extended Index::Column with:
std::string opclass;Extended Index with:
std::string index_type;Indexes table - Added column:
| Column | Position | Type |
|---|---|---|
| OPCLASS | 6 | TEXT |
IndexNames table - Added column:
| Column | Position | Type |
|---|---|---|
| INDEX_TYPE | 8 | TEXT |
Modified the index creation trigger to extract opclass and index type from PostgreSQL system catalogs:
SELECT
i.indexrelid AS index_oid,
i.indclass AS indclass,
am.amname AS index_type
FROM pg_index i
JOIN pg_class ic ON ic.oid = i.indexrelid
JOIN pg_am am ON am.oid = ic.relam
...
-- Extract opclass for each column
SELECT
opc.opcname AS opclass
FROM unnest(ind_obj.indkey, ind_obj.indclass)
WITH ORDINALITY AS u(attnum, opclass_oid, ord)
JOIN pg_opclass opc ON opc.oid = u.opclass_oidThis captures:
-
am.amname: The access method name (btree, gin, gist, brin) -
opc.opcname: The operator class name for each index column
Extended constructor to accept opclass handler and index type:
MutableBTree(uint64_t database_id,
const std::filesystem::path &file,
const std::vector<uint32_t> &keys,
ExtentSchemaPtr schema,
uint64_t xid,
uint64_t max_extent_size,
const ExtensionCallback &extension_callback = {},
const OpClassHandler &opclass_handler = {},
const std::string_view index_type = constant::INDEX_TYPE_BTREE);New member variables:
OpClassHandler _opclass_handler;
std::string_view _index_type;Extended create_index_root signature:
MutableBTreePtr create_index_root(
uint64_t index_id,
const std::vector<uint32_t>& index_columns,
const ExtensionCallback& extension_callback = {},
const OpClassHandler& opclass_handler = {},
const std::string_view index_type = constant::INDEX_TYPE_BTREE);Extended get_snapshot_table to accept OpClassHandler:
MutableTablePtr get_snapshot_table(
uint64_t db_id,
uint64_t table_id,
uint64_t snapshot_xid,
ExtentSchemaPtr schema,
const std::vector<Index>& secondary_keys,
const ExtensionCallback &extension_callback = {},
const OpClassHandler &opclass_handler = {});The indexer now branches based on index type:
if (idx._index_request.index().index_type() == constant::INDEX_TYPE_GIN) {
//XXX: Build GIN INDEX
} else {
// Default - btree index builder
root = mutable_table->create_index_root(index_id, idx_cols,
{PgExtnRegistry::get_instance()->comparator_func});
// ... existing B-tree build logic
}Similar branching exists for:
- Index invalidation during updates
- Index population during reconciliation
message IndexColumn {
string name = 1;
int32 position = 2;
int32 idx_position = 3;
string opclass = 4; // NEW
}
message IndexInfo {
...
string index_type = 10; // NEW
}PostgreSQL CREATE INDEX
↓
DDL Trigger (triggers.sql)
↓
Extract: opclass, index_type from pg_opclass, pg_am
↓
Replication Message (PgMsgIndex)
↓
sys_tbl_mgr::Server::_create_index()
↓
Store in IndexNames (index_type) and Indexes (opclass) system tables
↓
Indexer loads Index metadata (includes index_type, opclass per column)
↓
Indexer builds index based on index_type
↓
Create MutableBTree with OpClassHandler
↓
For GIN/GiST: invoke opclass methods via OpClassHandler
The core implementation is complete. The remaining blocker is a build failure in the unit test src/pg_fdw/test/where_test.cc.
Issue: The test file imports both table and mutable_table headers simultaneously. These headers have conflicting dependencies—one pulls in custom Springtail extension-related imports while the other includes default PostgreSQL imports, causing symbol conflicts during compilation.
Resolution: Avoid using mutable_table in the test. Instead, load the table data directly and use only the Table class for scan operations during testing.