reifydb-catalog 0.4.13

Database catalog and metadata management for ReifyDB
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

use reifydb_core::{
	event::EventBus,
	interface::catalog::{config::ConfigKey, id::NamespaceId},
};
use reifydb_runtime::context::clock::Clock;
use reifydb_transaction::{
	interceptor::interceptors::Interceptors,
	multi::transaction::MultiTransaction,
	single::SingleTransaction,
	transaction::{Transaction, admin::AdminTransaction, query::QueryTransaction},
};
use reifydb_type::value::{Value, identity::IdentityId};
use tracing::info;

use crate::{
	Result,
	catalog::{Catalog, namespace::NamespaceToCreate},
	materialized::{
		MaterializedCatalog,
		load::{MaterializedCatalogLoader, config::load_configs},
	},
};

pub mod binding;
pub mod identity;
pub mod metric;
pub mod procedure;

/// Bootstrap system objects: root identity, system procedures, metric ring buffers.
///
/// Must be called AFTER `load_materialized_catalog()`.
/// Callers that need CDC to capture bootstrap commits should ensure the CDC
/// producer is active before calling this function.
///
/// Skipped on replicas (they receive system objects via replication).
pub fn bootstrap_system_objects(
	multi: &MultiTransaction,
	single: &SingleTransaction,
	catalog: &MaterializedCatalog,
	eventbus: &EventBus,
) -> Result<()> {
	identity::bootstrap_root_identity(multi, single, catalog, eventbus)?;
	procedure::bootstrap_system_procedures(multi, single, catalog, eventbus)?;
	binding::bootstrap_system_bindings(multi, single, catalog, eventbus)?;
	metric::bootstrap_metric_ringbuffers(multi, single, catalog, eventbus)?;
	Ok(())
}

/// Apply builder-supplied system configuration overrides.
///
/// Each `(key, value)` pair is persisted via `Catalog::set_config`, overwriting any
/// existing override for that key. Reuses the existing validation and change-tracking
/// paths, so invalid values fail here exactly as they would at runtime.
///
/// Callers should skip this on replicas; configs propagate via replication.
pub fn apply_bootstrap_configs(
	multi: &MultiTransaction,
	single: &SingleTransaction,
	catalog: &MaterializedCatalog,
	eventbus: &EventBus,
	configs: &[(ConfigKey, Value)],
) -> Result<()> {
	if configs.is_empty() {
		return Ok(());
	}

	let mut admin = AdminTransaction::new(
		multi.clone(),
		single.clone(),
		eventbus.clone(),
		Interceptors::default(),
		IdentityId::system(),
		Clock::Real,
	)?;

	let catalog_api = Catalog::new(catalog.clone());
	for (key, value) in configs {
		catalog_api.set_config(&mut admin, *key, value.clone())?;
	}
	admin.commit()?;

	let mut qt = QueryTransaction::new(multi.begin_query()?, single.clone(), IdentityId::system());
	load_configs(&mut Transaction::Query(&mut qt), catalog)?;

	Ok(())
}

/// Load all catalog data from storage into MaterializedCatalog.
pub fn load_materialized_catalog(
	multi: &MultiTransaction,
	single: &SingleTransaction,
	catalog: &MaterializedCatalog,
) -> Result<()> {
	let mut qt = QueryTransaction::new(multi.begin_query()?, single.clone(), IdentityId::system());
	MaterializedCatalogLoader::load_all(&mut Transaction::Query(&mut qt), catalog)?;
	Ok(())
}

/// Find a namespace by its full `::`-separated path, or create it with the given id
/// and parent if it doesn't exist. Returns the resolved namespace id.
pub(crate) fn ensure_namespace(
	catalog_api: &Catalog,
	admin: &mut AdminTransaction,
	id: NamespaceId,
	path: &str,
	local_name: &str,
	parent_id: NamespaceId,
) -> Result<NamespaceId> {
	if let Some(ns) = catalog_api.find_namespace_by_path(&mut Transaction::Admin(admin), path)? {
		return Ok(ns.id());
	}

	let ns = catalog_api.create_namespace_with_id(
		admin,
		id,
		NamespaceToCreate {
			namespace_fragment: None,
			name: path.to_string(),
			local_name: local_name.to_string(),
			parent_id,
			token: None,
			grpc: None,
		},
	)?;
	info!("Created {} namespace", path);
	Ok(ns.id())
}