diff --git a/src/binder/create_index.rs b/src/binder/create_index.rs new file mode 100644 index 00000000..b3e93492 --- /dev/null +++ b/src/binder/create_index.rs @@ -0,0 +1,100 @@ +// Copyright 2025 RisingLight Project Authors. Licensed under Apache-2.0. + +use std::fmt; +use std::str::FromStr; + +use pretty_xmlish::helper::delegate_fmt; +use pretty_xmlish::Pretty; +use serde::{Deserialize, Serialize}; + +use super::*; +use crate::catalog::{ColumnId, SchemaId, TableId}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)] +pub struct CreateIndex { + pub schema_id: SchemaId, + pub index_name: String, + pub table_id: TableId, + pub columns: Vec, +} + +impl fmt::Display for CreateIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let explainer = Pretty::childless_record("CreateIndex", self.pretty_index()); + delegate_fmt(&explainer, f, String::with_capacity(1000)) + } +} + +impl CreateIndex { + pub fn pretty_index<'a>(&self) -> Vec<(&'a str, Pretty<'a>)> { + vec![ + ("schema_id", Pretty::display(&self.schema_id)), + ("name", Pretty::display(&self.index_name)), + ("table_id", Pretty::display(&self.table_id)), + ( + "columns", + Pretty::Array(self.columns.iter().map(Pretty::display).collect()), + ), + ] + } +} + +impl FromStr for Box { + type Err = (); + + fn from_str(_s: &str) -> std::result::Result { + Err(()) + } +} + +impl Binder { + pub(super) fn bind_create_index(&mut self, stat: crate::parser::CreateIndex) -> Result { + let Some(ref name) = stat.name else { + return Err( + ErrorKind::InvalidIndex("index must have a name".to_string()).with_spanned(&stat), + ); + }; + let crate::parser::CreateIndex { + table_name, + columns, + .. + } = stat; + let index_name = lower_case_name(name); + let (_, index_name) = split_name(&index_name)?; + let table_obj: ObjectName = table_name.clone(); + let table_name = lower_case_name(&table_name); + let (schema_name, table_name) = split_name(&table_name)?; + let schema = self + .catalog + .get_schema_by_name(schema_name) + .ok_or_else(|| ErrorKind::InvalidSchema(schema_name.into()).with_spanned(&table_obj))?; + let Some(table) = schema.get_table_by_name(table_name) else { + return Err(ErrorKind::InvalidTable(table_name.into()).with_spanned(&table_obj)); + }; + // Check if every column exists in the table and get the column ids + let mut column_ids = Vec::new(); + for column in &columns { + // Ensure column expr is a column reference + let OrderByExpr { expr, .. } = column; + let Expr::Identifier(column_name) = expr else { + return Err( + ErrorKind::InvalidColumn("column reference expected".to_string()) + .with_spanned(column), + ); + }; + let column_name = column_name.value.to_lowercase(); + let column_catalog = table + .get_column_by_name(&column_name) + .ok_or_else(|| ErrorKind::InvalidColumn(column_name).with_spanned(column))?; + column_ids.push(column_catalog.id()); + } + + let create = self.egraph.add(Node::CreateIndex(Box::new(CreateIndex { + schema_id: schema.id(), + index_name: index_name.into(), + table_id: table.id(), + columns: column_ids, + }))); + Ok(create) + } +} diff --git a/src/binder/error.rs b/src/binder/error.rs index 40711121..adae3a40 100644 --- a/src/binder/error.rs +++ b/src/binder/error.rs @@ -53,6 +53,8 @@ pub enum ErrorKind { InvalidSchema(String), #[error("invalid table {0:?}")] InvalidTable(String), + #[error("invalid index {0:?}")] + InvalidIndex(String), #[error("invalid column {0:?}")] InvalidColumn(String), #[error("table {0:?} already exists")] diff --git a/src/binder/mod.rs b/src/binder/mod.rs index 61fd2fe1..4b5d6fce 100644 --- a/src/binder/mod.rs +++ b/src/binder/mod.rs @@ -17,6 +17,7 @@ use crate::types::DataValue; pub mod copy; mod create_function; +mod create_index; mod create_table; mod create_view; mod delete; @@ -28,6 +29,7 @@ mod select; mod table; pub use self::create_function::CreateFunction; +pub use self::create_index::CreateIndex; pub use self::create_table::CreateTable; pub use self::error::BindError; use self::error::ErrorKind; @@ -226,6 +228,7 @@ impl Binder { fn bind_stmt(&mut self, stmt: Statement) -> Result { match stmt { + Statement::CreateIndex(create_index) => self.bind_create_index(create_index), Statement::CreateTable(create_table) => self.bind_create_table(create_table), Statement::CreateView { name, diff --git a/src/catalog/index.rs b/src/catalog/index.rs new file mode 100644 index 00000000..42574378 --- /dev/null +++ b/src/catalog/index.rs @@ -0,0 +1,38 @@ +// Copyright 2025 RisingLight Project Authors. Licensed under Apache-2.0. + +use super::*; + +/// The catalog of an index. +pub struct IndexCatalog { + id: IndexId, + name: String, + table_id: TableId, + column_idxs: Vec, +} + +impl IndexCatalog { + pub fn new(id: IndexId, name: String, table_id: TableId, column_idxs: Vec) -> Self { + Self { + id, + name, + table_id, + column_idxs, + } + } + + pub fn table_id(&self) -> TableId { + self.table_id + } + + pub fn column_idxs(&self) -> &[ColumnId] { + &self.column_idxs + } + + pub fn id(&self) -> IndexId { + self.id + } + + pub fn name(&self) -> &str { + &self.name + } +} diff --git a/src/catalog/mod.rs b/src/catalog/mod.rs index abae2945..2be2f1f1 100644 --- a/src/catalog/mod.rs +++ b/src/catalog/mod.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; pub use self::column::*; +pub use self::index::*; pub use self::root::*; pub use self::schema::*; pub use self::table::*; @@ -13,12 +14,14 @@ use crate::types::*; mod column; pub mod function; +mod index; mod root; mod schema; mod table; pub type SchemaId = u32; pub type TableId = u32; +pub type IndexId = u32; pub type ColumnId = u32; pub type RootCatalogRef = Arc; diff --git a/src/catalog/root.rs b/src/catalog/root.rs index aa4ca642..d54a35f1 100644 --- a/src/catalog/root.rs +++ b/src/catalog/root.rs @@ -98,6 +98,34 @@ impl RootCatalog { schema.add_view(name, columns, query) } + pub fn add_index( + &self, + schema_id: SchemaId, + index_name: String, + table_id: TableId, + column_idxs: &[ColumnId], + ) -> Result { + let mut inner = self.inner.lock().unwrap(); + let schema = inner.schemas.get_mut(&schema_id).unwrap(); + schema.add_index(index_name, table_id, column_idxs.to_vec()) + } + + pub fn get_index_on_table(&self, schema_id: SchemaId, table_id: TableId) -> Vec { + let mut inner = self.inner.lock().unwrap(); + let schema = inner.schemas.get_mut(&schema_id).unwrap(); + schema.get_indexes_on_table(table_id) + } + + pub fn get_index_by_id( + &self, + schema_id: SchemaId, + index_id: IndexId, + ) -> Option> { + let mut inner = self.inner.lock().unwrap(); + let schema = inner.schemas.get_mut(&schema_id).unwrap(); + schema.get_index_by_id(index_id) + } + pub fn drop_table(&self, table_ref_id: TableRefId) { let mut inner = self.inner.lock().unwrap(); let schema = inner.schemas.get_mut(&table_ref_id.schema_id).unwrap(); @@ -208,6 +236,15 @@ const CREATE_SYSTEM_TABLE_SQL: &str = " table_id int not null, table_name string not null ); + create table pg_indexes ( + schema_id int not null, + schema_name string not null, + table_id int not null, + table_name string not null, + index_id int not null, + index_name string not null, + on_columns string not null + ); create table pg_attribute ( schema_name string not null, table_name string not null, diff --git a/src/catalog/schema.rs b/src/catalog/schema.rs index b33547d3..5a6a81d1 100644 --- a/src/catalog/schema.rs +++ b/src/catalog/schema.rs @@ -14,7 +14,9 @@ pub struct SchemaCatalog { name: String, table_idxs: HashMap, tables: HashMap>, - next_table_id: TableId, + indexes_idxs: HashMap, + indexes: HashMap>, + next_id: u32, /// Currently indexed by function name functions: HashMap>, } @@ -26,7 +28,9 @@ impl SchemaCatalog { name, table_idxs: HashMap::new(), tables: HashMap::new(), - next_table_id: 0, + indexes_idxs: HashMap::new(), + indexes: HashMap::new(), + next_id: 0, functions: HashMap::new(), } } @@ -40,8 +44,8 @@ impl SchemaCatalog { if self.table_idxs.contains_key(&name) { return Err(CatalogError::Duplicated("table", name)); } - let table_id = self.next_table_id; - self.next_table_id += 1; + let table_id = self.next_id; + self.next_id += 1; let table_catalog = Arc::new(TableCatalog::new( table_id, name.clone(), @@ -53,6 +57,23 @@ impl SchemaCatalog { Ok(table_id) } + pub(super) fn add_index( + &mut self, + name: String, + table_id: TableId, + columns: Vec, + ) -> Result { + if self.indexes_idxs.contains_key(&name) { + return Err(CatalogError::Duplicated("index", name)); + } + let index_id = self.next_id; + self.next_id += 1; + let index_catalog = Arc::new(IndexCatalog::new(index_id, name.clone(), table_id, columns)); + self.indexes_idxs.insert(name, index_id); + self.indexes.insert(index_id, index_catalog); + Ok(index_id) + } + pub(super) fn add_view( &mut self, name: String, @@ -62,8 +83,8 @@ impl SchemaCatalog { if self.table_idxs.contains_key(&name) { return Err(CatalogError::Duplicated("view", name)); } - let table_id = self.next_table_id; - self.next_table_id += 1; + let table_id = self.next_id; + self.next_id += 1; let table_catalog = Arc::new(TableCatalog::new_view( table_id, name.clone(), @@ -84,6 +105,10 @@ impl SchemaCatalog { self.tables.clone() } + pub fn all_indexes(&self) -> HashMap> { + self.indexes.clone() + } + pub fn get_table_id_by_name(&self, name: &str) -> Option { self.table_idxs.get(name).cloned() } @@ -92,6 +117,18 @@ impl SchemaCatalog { self.tables.get(&table_id).cloned() } + pub fn get_indexes_on_table(&self, table_id: TableId) -> Vec { + self.indexes + .iter() + .filter(|(_, index)| index.table_id() == table_id) + .map(|(id, _)| *id) + .collect() + } + + pub fn get_index_by_id(&self, index_id: IndexId) -> Option> { + self.indexes.get(&index_id).cloned() + } + pub fn get_table_by_name(&self, name: &str) -> Option> { self.table_idxs .get(name) diff --git a/src/db.rs b/src/db.rs index a2e7b442..f4399c0d 100644 --- a/src/db.rs +++ b/src/db.rs @@ -65,6 +65,7 @@ impl Database { let tokens = cmd.split_whitespace().collect::>(); Ok(match tokens.as_slice() { ["dt"] => "SELECT * FROM pg_catalog.pg_tables".to_string(), + ["di"] => "SELECT * FROM pg_catalog.pg_indexes".to_string(), ["d", table] => format!( "SELECT * FROM pg_catalog.pg_attribute WHERE table_name = '{table}'", ), diff --git a/src/executor/create_index.rs b/src/executor/create_index.rs new file mode 100644 index 00000000..a1ac1576 --- /dev/null +++ b/src/executor/create_index.rs @@ -0,0 +1,29 @@ +// Copyright 2025 RisingLight Project Authors. Licensed under Apache-2.0. + +use std::sync::Arc; + +use super::*; +use crate::binder::CreateIndex; +use crate::storage::Storage; + +/// The executor of `create index` statement. +pub struct CreateIndexExecutor { + pub index: Box, + pub storage: Arc, +} + +impl CreateIndexExecutor { + #[try_stream(boxed, ok = DataChunk, error = ExecutorError)] + pub async fn execute(self) { + self.storage + .create_index( + self.index.schema_id, + &self.index.index_name, + self.index.table_id, + &self.index.columns, + ) + .await?; + + yield DataChunk::single(1); + } +} diff --git a/src/executor/mod.rs b/src/executor/mod.rs index c32900d6..244c0ead 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -27,6 +27,7 @@ use self::analyze::*; use self::copy_from_file::*; use self::copy_to_file::*; use self::create_function::*; +use self::create_index::*; use self::create_table::*; use self::create_view::*; use self::delete::*; @@ -79,6 +80,7 @@ mod nested_loop_join; mod order; mod system_table_scan; // mod perfect_hash_agg; +mod create_index; mod error; mod merge_join; mod projection; @@ -392,6 +394,12 @@ impl Builder { } .execute(), + CreateIndex(index) => CreateIndexExecutor { + index, + storage: self.storage.clone(), + } + .execute(), + CreateView([table, query]) => CreateViewExecutor { table: self.node(table).as_create_table(), query: self.recexpr(query), diff --git a/src/executor/system_table_scan.rs b/src/executor/system_table_scan.rs index 30119f30..49fe7a16 100644 --- a/src/executor/system_table_scan.rs +++ b/src/executor/system_table_scan.rs @@ -27,6 +27,7 @@ impl SystemTableScan { yield match table.name() { "contributors" => contributors(), "pg_tables" => pg_tables(self.catalog), + "pg_indexes" => pg_indexes(self.catalog), "pg_attribute" => pg_attribute(self.catalog), "pg_stat" => pg_stat(self.catalog, &*self.storage).await?, name => panic!("unknown system table: {:?}", name), @@ -104,6 +105,51 @@ fn contributors() -> DataChunk { .collect() } +/// Returns `pg_indexes` table. +fn pg_indexes(catalog: RootCatalogRef) -> DataChunk { + let mut schema_id = I32ArrayBuilder::new(); + let mut index_id = I32ArrayBuilder::new(); + let mut table_id = I32ArrayBuilder::new(); + let mut schema_name = StringArrayBuilder::new(); + let mut table_name = StringArrayBuilder::new(); + let mut index_name = StringArrayBuilder::new(); + let mut on_columns = StringArrayBuilder::new(); + + for (_, schema) in catalog.all_schemas() { + for (_, table) in schema.all_tables() { + for index in schema.get_indexes_on_table(table.id()) { + let index = schema.get_index_by_id(index).unwrap(); + schema_id.push(Some(&(schema.id() as i32))); + table_id.push(Some(&(table.id() as i32))); + index_id.push(Some(&(index.id() as i32))); + schema_name.push(Some(&schema.name())); + table_name.push(Some(table.name())); + index_name.push(Some(index.name())); + on_columns.push(Some(&format!( + "[{}]", + index + .column_idxs() + .iter() + .map(|c| c.to_string()) + .collect::>() + .join(",") + ))); + } + } + } + [ + ArrayBuilderImpl::from(schema_id), + schema_name.into(), + ArrayBuilderImpl::from(table_id), + table_name.into(), + ArrayBuilderImpl::from(index_id), + index_name.into(), + on_columns.into(), + ] + .into_iter() + .collect() +} + /// Returns `pg_tables` table. fn pg_tables(catalog: RootCatalogRef) -> DataChunk { let mut schema_id = I32ArrayBuilder::new(); diff --git a/src/planner/explain.rs b/src/planner/explain.rs index efabd09f..cbb83f59 100644 --- a/src/planner/explain.rs +++ b/src/planner/explain.rs @@ -326,6 +326,10 @@ impl<'a> Explain<'a> { let fields = with_meta(t.pretty_table()); Pretty::childless_record("CreateTable", fields) } + CreateIndex(i) => { + let fields = with_meta(i.pretty_index()); + Pretty::childless_record("CreateIndex", fields) + } CreateView([table, query]) => Pretty::simple_record( "CreateView", with_meta(vec![("table", self.expr(table).pretty())]), diff --git a/src/planner/mod.rs b/src/planner/mod.rs index e00cd261..a573e2f3 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -3,7 +3,7 @@ use egg::{define_language, Id, Symbol}; use crate::binder::copy::ExtSource; -use crate::binder::{CreateFunction, CreateTable}; +use crate::binder::{CreateFunction, CreateIndex, CreateTable}; use crate::catalog::{ColumnRefId, TableRefId}; use crate::parser::{BinaryOperator, UnaryOperator}; use crate::types::{ColumnIndex, DataType, DataValue, DateTimeField}; @@ -119,6 +119,7 @@ define_language! { "window" = Window([Id; 2]), // (window [over..] child) // output = child || exprs CreateTable(Box), + CreateIndex(Box), "create_view" = CreateView([Id; 2]), // (create_view create_table child) CreateFunction(CreateFunction), "drop" = Drop(Id), // (drop [table..]) diff --git a/src/storage/index/mod.rs b/src/storage/index/mod.rs new file mode 100644 index 00000000..ae4d9855 --- /dev/null +++ b/src/storage/index/mod.rs @@ -0,0 +1,34 @@ +// Copyright 2025 RisingLight Project Authors. Licensed under Apache-2.0. + +use std::sync::Arc; + +use crate::catalog::{ColumnId, IndexId, SchemaId, TableId}; + +pub trait InMemoryIndex: 'static + Send + Sync {} + +pub struct InMemoryIndexes {} + +impl InMemoryIndexes { + pub fn new() -> Self { + Self {} + } + + pub fn add_index( + &self, + schema_id: SchemaId, + index_id: IndexId, + table_id: TableId, + column_idxs: &[ColumnId], + ) { + let _ = (schema_id, index_id, table_id, column_idxs); + } + + pub fn get_index( + &self, + schema_id: SchemaId, + index_id: IndexId, + ) -> Option> { + let _ = (schema_id, index_id); + None + } +} diff --git a/src/storage/memory/mod.rs b/src/storage/memory/mod.rs index 3d508400..6d7a37f7 100644 --- a/src/storage/memory/mod.rs +++ b/src/storage/memory/mod.rs @@ -20,8 +20,11 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use super::{Storage, StorageError, StorageResult, TracedStorageError}; -use crate::catalog::{ColumnCatalog, ColumnId, RootCatalog, RootCatalogRef, SchemaId, TableRefId}; +use super::index::InMemoryIndexes; +use super::{InMemoryIndex, Storage, StorageError, StorageResult, TracedStorageError}; +use crate::catalog::{ + ColumnCatalog, ColumnId, IndexId, RootCatalog, RootCatalogRef, SchemaId, TableId, TableRefId, +}; mod table; pub use table::InMemoryTable; @@ -39,6 +42,7 @@ pub use row_handler::InMemoryRowHandler; pub struct InMemoryStorage { catalog: RootCatalogRef, tables: Mutex>, + indexes: Mutex, } impl Default for InMemoryStorage { @@ -52,6 +56,7 @@ impl InMemoryStorage { InMemoryStorage { catalog: Arc::new(RootCatalog::new()), tables: Mutex::new(HashMap::new()), + indexes: Mutex::new(InMemoryIndexes::new()), } } @@ -121,4 +126,41 @@ impl Storage for InMemoryStorage { fn as_disk(&self) -> Option<&super::SecondaryStorage> { None } + + async fn create_index( + &self, + schema_id: SchemaId, + index_name: &str, + table_id: TableId, + column_idxs: &[ColumnId], + ) -> StorageResult { + let idx_id = self + .catalog + .add_index(schema_id, index_name.to_string(), table_id, column_idxs) + .map_err(|_| StorageError::Duplicated("index", index_name.into()))?; + self.indexes + .lock() + .unwrap() + .add_index(schema_id, idx_id, table_id, column_idxs); + // TODO: populate the index + Ok(idx_id) + } + + async fn get_index( + &self, + schema_id: SchemaId, + index_id: IndexId, + ) -> StorageResult> { + let idx = self + .indexes + .lock() + .unwrap() + .get_index(schema_id, index_id) + .ok_or_else(|| StorageError::NotFound("index", index_id.to_string()))?; + Ok(idx) + } + + fn get_catalog(&self) -> Arc { + self.catalog.clone() + } } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index c51c774e..8eacafab 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -8,6 +8,9 @@ pub use memory::InMemoryStorage; mod secondary; pub use secondary::{SecondaryStorage, StorageOptions as SecondaryStorageOptions}; +mod index; +pub use index::InMemoryIndex; + mod error; pub use error::{StorageError, StorageResult, TracedStorageError}; use serde::Serialize; @@ -21,7 +24,9 @@ pub use chunk::*; use enum_dispatch::enum_dispatch; use crate::array::{ArrayImpl, DataChunk}; -use crate::catalog::{ColumnCatalog, ColumnId, SchemaId, TableRefId}; +use crate::catalog::{ + ColumnCatalog, ColumnId, IndexId, RootCatalog, SchemaId, TableId, TableRefId, +}; use crate::types::DataValue; #[enum_dispatch(StorageDispatch)] @@ -82,6 +87,25 @@ pub trait Storage: Sync + Send + 'static { fn drop_table(&self, table_id: TableRefId) -> impl Future> + Send; + fn create_index( + &self, + schema_id: SchemaId, + index_name: &str, + table_id: TableId, + column_idxs: &[ColumnId], + ) -> impl Future> + Send; + + /// Get the catalog of the storage engine. + /// + /// TODO: users should not be able to modify the catalog. + fn get_catalog(&self) -> Arc; + + fn get_index( + &self, + schema_id: SchemaId, + index_id: IndexId, + ) -> impl Future>> + Send; + // XXX: remove this fn as_disk(&self) -> Option<&SecondaryStorage>; } diff --git a/src/storage/secondary/mod.rs b/src/storage/secondary/mod.rs index b9a5cb01..c6d4705d 100644 --- a/src/storage/secondary/mod.rs +++ b/src/storage/secondary/mod.rs @@ -32,8 +32,11 @@ use transaction_manager::*; pub use txn_iterator::*; use version_manager::*; -use super::{Storage, StorageResult, TracedStorageError}; -use crate::catalog::{ColumnCatalog, ColumnId, RootCatalogRef, SchemaId, TableRefId}; +use super::index::InMemoryIndexes; +use super::{InMemoryIndex, Storage, StorageError, StorageResult, TracedStorageError}; +use crate::catalog::{ + ColumnCatalog, ColumnId, IndexId, RootCatalog, RootCatalogRef, SchemaId, TableId, TableRefId, +}; // public modules and structures mod options; @@ -98,6 +101,9 @@ pub struct SecondaryStorage { /// Manages all ongoing txns txn_mgr: Arc, + + /// Indexes of the current storage engine + indexes: Mutex, } impl SecondaryStorage { @@ -187,4 +193,41 @@ impl Storage for SecondaryStorage { fn as_disk(&self) -> Option<&SecondaryStorage> { Some(self) } + + async fn create_index( + &self, + schema_id: SchemaId, + index_name: &str, + table_id: TableId, + column_idxs: &[ColumnId], + ) -> StorageResult { + let idx_id = self + .catalog + .add_index(schema_id, index_name.to_string(), table_id, column_idxs) + .map_err(|_| StorageError::Duplicated("index", index_name.into()))?; + self.indexes + .lock() + .await + .add_index(schema_id, idx_id, table_id, column_idxs); + // TODO: populate the index + Ok(idx_id) + } + + async fn get_index( + &self, + schema_id: SchemaId, + index_id: IndexId, + ) -> StorageResult> { + let idx = self + .indexes + .lock() + .await + .get_index(schema_id, index_id) + .ok_or_else(|| StorageError::NotFound("index", index_id.to_string()))?; + Ok(idx) + } + + fn get_catalog(&self) -> Arc { + self.catalog.clone() + } } diff --git a/src/storage/secondary/storage.rs b/src/storage/secondary/storage.rs index 91f397a4..3d0562c9 100644 --- a/src/storage/secondary/storage.rs +++ b/src/storage/secondary/storage.rs @@ -12,6 +12,7 @@ use tracing::info; use super::{DiskRowset, Manifest, SecondaryStorage, StorageOptions, StorageResult}; use crate::catalog::RootCatalog; +use crate::storage::index::InMemoryIndexes; use crate::storage::secondary::manifest::*; use crate::storage::secondary::transaction_manager::TransactionManager; use crate::storage::secondary::version_manager::{EpochOp, VersionManager}; @@ -58,6 +59,7 @@ impl SecondaryStorage { compactor_handler: Mutex::new((None, None)), vacuum_handler: Mutex::new((None, None)), txn_mgr: Arc::new(TransactionManager::default()), + indexes: Mutex::new(InMemoryIndexes::new()), }; info!("applying {} manifest entries", manifest_ops.len()); diff --git a/tests/sql/catalog.slt b/tests/sql/catalog.slt index 7db803bb..11f97694 100644 --- a/tests/sql/catalog.slt +++ b/tests/sql/catalog.slt @@ -6,6 +6,15 @@ query ITIT rowsort ---- 0 pg_catalog 0 contributors 0 pg_catalog 1 pg_tables -0 pg_catalog 2 pg_attribute -0 pg_catalog 3 pg_stat +0 pg_catalog 2 pg_indexes +0 pg_catalog 3 pg_attribute +0 pg_catalog 4 pg_stat 1 postgres 0 t + +statement ok +create index i1 on t(v1) + +query ITITITT rowsort +\di +---- +1 postgres 0 t 1 i1 [0]