use vibesql_ast::{CreateIndexStmt, VectorDistanceMetric};
use vibesql_catalog::TableSchema;
use vibesql_storage::Database;
use vibesql_types::DataType;
use super::btree_index::index_name_to_id;
use crate::errors::ExecutorError;
pub fn create_ivfflat_index(
database: &mut Database,
stmt: &CreateIndexStmt,
table_name: &str,
qualified_table_name: &str,
table_schema: &TableSchema,
metric: VectorDistanceMetric,
lists: u32,
) -> Result<String, ExecutorError> {
let index_name = &stmt.index_name;
if stmt.columns.len() != 1 {
return Err(ExecutorError::InvalidIndexDefinition(
"IVFFlat indexes must be defined on exactly one vector column".to_string(),
));
}
let column_name = stmt.columns[0].expect_column_name();
let col_idx = table_schema.get_column_index(column_name).ok_or_else(|| {
ExecutorError::ColumnNotFound {
column_name: column_name.to_string(),
table_name: qualified_table_name.to_string(),
searched_tables: vec![qualified_table_name.to_string()],
available_columns: table_schema.columns.iter().map(|c| c.name.clone()).collect(),
}
})?;
let col_type = &table_schema.columns[col_idx].data_type;
let dimensions = match col_type {
DataType::Vector { dimensions } => *dimensions as usize,
_ => {
return Err(ExecutorError::InvalidIndexDefinition(format!(
"IVFFlat indexes can only be created on VECTOR columns, but '{}' has type {:?}",
column_name, col_type
)));
}
};
let catalog_metric = convert_metric_to_catalog(metric);
let index_metadata = vibesql_catalog::IndexMetadata::new(
index_name.clone(),
table_name.to_string(),
vibesql_catalog::IndexType::IVFFlat { metric: catalog_metric, lists },
vec![vibesql_catalog::IndexedColumn::new_column(
column_name.to_string(),
vibesql_catalog::SortOrder::Ascending, )],
false, )
.with_schema(super::schema_of_qualified(qualified_table_name));
database.catalog.add_index(index_metadata)?;
database.create_ivfflat_index(
index_name.clone(),
table_name.to_string(),
column_name.to_string(),
col_idx,
dimensions,
lists as usize,
metric,
)?;
database.emit_wal_create_index(
index_name_to_id(index_name),
index_name,
qualified_table_name,
vec![col_idx as u32],
false,
);
Ok(format!(
"IVFFlat index '{}' created successfully on table '{}' column '{}'",
index_name, qualified_table_name, column_name
))
}
pub fn create_hnsw_index(
database: &mut Database,
stmt: &CreateIndexStmt,
table_name: &str,
qualified_table_name: &str,
table_schema: &TableSchema,
metric: VectorDistanceMetric,
m: u32,
ef_construction: u32,
) -> Result<String, ExecutorError> {
let index_name = &stmt.index_name;
if stmt.columns.len() != 1 {
return Err(ExecutorError::InvalidIndexDefinition(
"HNSW indexes must be defined on exactly one vector column".to_string(),
));
}
let column_name = stmt.columns[0].expect_column_name();
let col_idx = table_schema.get_column_index(column_name).ok_or_else(|| {
ExecutorError::ColumnNotFound {
column_name: column_name.to_string(),
table_name: qualified_table_name.to_string(),
searched_tables: vec![qualified_table_name.to_string()],
available_columns: table_schema.columns.iter().map(|c| c.name.clone()).collect(),
}
})?;
let col_type = &table_schema.columns[col_idx].data_type;
let dimensions = match col_type {
DataType::Vector { dimensions } => *dimensions as usize,
_ => {
return Err(ExecutorError::InvalidIndexDefinition(format!(
"HNSW indexes can only be created on VECTOR columns, but '{}' has type {:?}",
column_name, col_type
)));
}
};
let catalog_metric = convert_metric_to_catalog(metric);
let index_metadata = vibesql_catalog::IndexMetadata::new(
index_name.clone(),
table_name.to_string(),
vibesql_catalog::IndexType::Hnsw { metric: catalog_metric, m, ef_construction },
vec![vibesql_catalog::IndexedColumn::new_column(
column_name.to_string(),
vibesql_catalog::SortOrder::Ascending, )],
false, )
.with_schema(super::schema_of_qualified(qualified_table_name));
database.catalog.add_index(index_metadata)?;
database.create_hnsw_index(
index_name.clone(),
table_name.to_string(),
column_name.to_string(),
col_idx,
dimensions,
m,
ef_construction,
metric,
)?;
database.emit_wal_create_index(
index_name_to_id(index_name),
index_name,
qualified_table_name,
vec![col_idx as u32],
false,
);
Ok(format!(
"HNSW index '{}' created successfully on table '{}' column '{}'",
index_name, qualified_table_name, column_name
))
}
fn convert_metric_to_catalog(
metric: VectorDistanceMetric,
) -> vibesql_catalog::VectorDistanceMetric {
match metric {
VectorDistanceMetric::L2 => vibesql_catalog::VectorDistanceMetric::L2,
VectorDistanceMetric::Cosine => vibesql_catalog::VectorDistanceMetric::Cosine,
VectorDistanceMetric::InnerProduct => vibesql_catalog::VectorDistanceMetric::InnerProduct,
}
}