reifydb_catalog/store/column/
get.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use reifydb_core::{
5	Error,
6	interface::{ColumnsKey, DictionaryId, QueryTransaction},
7};
8use reifydb_type::{Constraint, Type, TypeConstraint, internal};
9
10use crate::store::column::layout::column::LAYOUT;
11
12/// Decodes a constraint from stored bytes
13fn decode_constraint(bytes: &[u8]) -> Option<Constraint> {
14	if bytes.is_empty() {
15		return None;
16	}
17
18	match bytes[0] {
19		0 => None, // No constraint
20		1 if bytes.len() >= 5 => {
21			// MaxBytes constraint
22			let max_bytes = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
23			Some(Constraint::MaxBytes(max_bytes.into()))
24		}
25		2 if bytes.len() >= 3 => {
26			// PrecisionScale constraint
27			let precision = bytes[1];
28			let scale = bytes[2];
29			Some(Constraint::PrecisionScale(precision.into(), scale.into()))
30		}
31		_ => None, // Unknown or invalid constraint type
32	}
33}
34
35use crate::{
36	CatalogStore,
37	store::column::{
38		ColumnDef, ColumnId, ColumnIndex,
39		layout::column::{AUTO_INCREMENT, CONSTRAINT, DICTIONARY_ID, ID, INDEX, NAME, VALUE},
40	},
41};
42
43impl CatalogStore {
44	pub async fn get_column(rx: &mut impl QueryTransaction, column: ColumnId) -> crate::Result<ColumnDef> {
45		let multi = rx.get(&ColumnsKey::encoded(column)).await?.ok_or_else(|| {
46			Error(internal!(
47				"Table column with ID {:?} not found in catalog. This indicates a critical catalog inconsistency.",
48				column
49			))
50		})?;
51
52		let row = multi.values;
53
54		let id = ColumnId(LAYOUT.get_u64(&row, ID));
55		let name = LAYOUT.get_utf8(&row, NAME).to_string();
56		let base_type = Type::from_u8(LAYOUT.get_u8(&row, VALUE));
57		let index = ColumnIndex(LAYOUT.get_u8(&row, INDEX));
58		let auto_increment = LAYOUT.get_bool(&row, AUTO_INCREMENT);
59
60		// Reconstruct constraint from stored blob
61		let constraint_bytes = LAYOUT.get_blob(&row, CONSTRAINT);
62		let constraint = match decode_constraint(constraint_bytes.as_bytes()) {
63			Some(c) => TypeConstraint::with_constraint(base_type, c),
64			None => TypeConstraint::unconstrained(base_type),
65		};
66
67		// Read dictionary_id (0 means no dictionary)
68		let dict_id_raw = LAYOUT.get_u64(&row, DICTIONARY_ID);
69		let dictionary_id = if dict_id_raw == 0 {
70			None
71		} else {
72			Some(DictionaryId(dict_id_raw))
73		};
74
75		let policies = Self::list_column_policies(rx, id).await?;
76
77		Ok(ColumnDef {
78			id,
79			name,
80			constraint,
81			index,
82			policies,
83			auto_increment,
84			dictionary_id,
85		})
86	}
87}
88
89#[cfg(test)]
90mod tests {
91	use reifydb_core::interface::ColumnId;
92	use reifydb_engine::test_utils::create_test_command_transaction;
93	use reifydb_type::{Type, TypeConstraint};
94
95	use crate::{CatalogStore, test_utils::create_test_column};
96
97	#[tokio::test]
98	async fn test_ok() {
99		let mut txn = create_test_command_transaction().await;
100		create_test_column(&mut txn, "col_1", TypeConstraint::unconstrained(Type::Int1), vec![]).await;
101		create_test_column(&mut txn, "col_2", TypeConstraint::unconstrained(Type::Int2), vec![]).await;
102		create_test_column(&mut txn, "col_3", TypeConstraint::unconstrained(Type::Int4), vec![]).await;
103
104		let result = CatalogStore::get_column(&mut txn, ColumnId(8194)).await.unwrap();
105
106		assert_eq!(result.id, ColumnId(8194));
107		assert_eq!(result.name, "col_2");
108		assert_eq!(result.constraint.get_type(), Type::Int2);
109		assert_eq!(result.auto_increment, false);
110	}
111
112	#[tokio::test]
113	async fn test_not_found() {
114		let mut txn = create_test_command_transaction().await;
115		create_test_column(&mut txn, "col_1", TypeConstraint::unconstrained(Type::Int1), vec![]).await;
116		create_test_column(&mut txn, "col_2", TypeConstraint::unconstrained(Type::Int2), vec![]).await;
117		create_test_column(&mut txn, "col_3", TypeConstraint::unconstrained(Type::Int4), vec![]).await;
118
119		let err = CatalogStore::get_column(&mut txn, ColumnId(4)).await.unwrap_err();
120		assert_eq!(err.code, "INTERNAL_ERROR");
121		assert!(err.message.contains("ColumnId(4)"));
122		assert!(err.message.contains("not found in catalog"));
123	}
124}