Skip to main content

reifydb_routine/procedure/set/
config.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{str::FromStr, sync::LazyLock};
5
6use reifydb_catalog::error::CatalogError;
7use reifydb_core::{interface::catalog::config::ConfigKey, value::column::columns::Columns};
8use reifydb_transaction::transaction::Transaction;
9use reifydb_type::{
10	error::Error as TypeError,
11	fragment::Fragment,
12	params::Params,
13	value::{Value, r#type::Type},
14};
15
16use crate::routine::{Routine, RoutineInfo, context::ProcedureContext, error::RoutineError};
17
18static INFO: LazyLock<RoutineInfo> = LazyLock::new(|| RoutineInfo::new("system::config::set"));
19
20pub struct SetConfigProcedure;
21
22impl Default for SetConfigProcedure {
23	fn default() -> Self {
24		Self::new()
25	}
26}
27
28impl SetConfigProcedure {
29	pub fn new() -> Self {
30		Self
31	}
32}
33
34impl<'a, 'tx> Routine<ProcedureContext<'a, 'tx>> for SetConfigProcedure {
35	fn info(&self) -> &RoutineInfo {
36		&INFO
37	}
38
39	fn return_type(&self, _input_types: &[Type]) -> Type {
40		Type::Any
41	}
42
43	fn execute(&self, ctx: &mut ProcedureContext<'a, 'tx>, _args: &Columns) -> Result<Columns, RoutineError> {
44		let (key, value) = match ctx.params {
45			Params::Positional(args) if args.len() == 2 => (args[0].clone(), args[1].clone()),
46			Params::Positional(args) => {
47				return Err(RoutineError::ProcedureArityMismatch {
48					procedure: Fragment::internal("system::config::set"),
49					expected: 2,
50					actual: args.len(),
51				});
52			}
53			_ => {
54				return Err(RoutineError::ProcedureArityMismatch {
55					procedure: Fragment::internal("system::config::set"),
56					expected: 2,
57					actual: 0,
58				});
59			}
60		};
61
62		let key_str = match &key {
63			Value::Utf8(s) => s.as_str().to_string(),
64			_ => {
65				return Err(RoutineError::ProcedureInvalidArgumentType {
66					procedure: Fragment::internal("system::config::set"),
67					argument_index: 0,
68					expected: vec![Type::Utf8],
69					actual: key.get_type(),
70				});
71			}
72		};
73
74		if matches!(value, Value::None { .. }) {
75			return Err(CatalogError::ConfigValueInvalid(key_str).into());
76		}
77
78		let config_key = match ConfigKey::from_str(&key_str) {
79			Ok(k) => k,
80			Err(_) => {
81				return Err(CatalogError::ConfigStorageKeyNotFound(key_str).into());
82			}
83		};
84
85		let coerced_value = config_key.accept(value).map_err(|e| {
86			RoutineError::Wrapped(Box::new(TypeError::from(CatalogError::from((config_key, e)))))
87		})?;
88
89		let value_clone = coerced_value.clone();
90
91		match ctx.tx {
92			Transaction::Admin(admin) => ctx.catalog.set_config(admin, config_key, coerced_value)?,
93			Transaction::Test(t) => ctx.catalog.set_config(t.inner, config_key, coerced_value)?,
94			_ => {
95				return Err(RoutineError::ProcedureExecutionFailed {
96					procedure: Fragment::internal("system::config::set"),
97					reason: "must run in an admin transaction".to_string(),
98				});
99			}
100		}
101
102		Ok(Columns::single_row([("key", Value::Utf8(key_str)), ("value", value_clone)]))
103	}
104}