reifydb_catalog/store/column/
list.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::interface::{ColumnKey, QueryTransaction, SourceId};
5
6use crate::{
7	CatalogStore,
8	store::column::{ColumnDef, ColumnId, layout::source_column},
9};
10
11/// Extended column information for system catalogs
12pub struct ColumnInfo {
13	pub column: ColumnDef,
14	pub source_id: SourceId,
15	pub is_view: bool,
16}
17
18impl CatalogStore {
19	pub async fn list_columns(
20		rx: &mut impl QueryTransaction,
21		source: impl Into<SourceId>,
22	) -> crate::Result<Vec<ColumnDef>> {
23		let source = source.into();
24		let mut result = vec![];
25
26		let batch = rx.range(ColumnKey::full_scan(source)).await?;
27		let ids: Vec<_> = batch
28			.items
29			.into_iter()
30			.map(|multi| {
31				let row = multi.values;
32				ColumnId(source_column::LAYOUT.get_u64(&row, source_column::ID))
33			})
34			.collect();
35
36		for id in ids {
37			result.push(Self::get_column(rx, id).await?);
38		}
39
40		result.sort_by_key(|c| c.index);
41
42		Ok(result)
43	}
44
45	pub async fn list_columns_all(rx: &mut impl QueryTransaction) -> crate::Result<Vec<ColumnInfo>> {
46		let mut result = Vec::new();
47
48		// Get all tables
49		let tables = CatalogStore::list_tables_all(rx).await?;
50		for table in tables {
51			let columns = CatalogStore::list_columns(rx, table.id).await?;
52			for column in columns {
53				result.push(ColumnInfo {
54					column,
55					source_id: table.id.into(),
56					is_view: false,
57				});
58			}
59		}
60
61		// Get all views
62		let views = CatalogStore::list_views_all(rx).await?;
63		for view in views {
64			let columns = CatalogStore::list_columns(rx, view.id).await?;
65			for column in columns {
66				result.push(ColumnInfo {
67					column,
68					source_id: view.id.into(),
69					is_view: true,
70				});
71			}
72		}
73
74		// Get all ring buffers
75		let ringbuffers = CatalogStore::list_ringbuffers_all(rx).await?;
76		for ringbuffer in ringbuffers {
77			let columns = CatalogStore::list_columns(rx, ringbuffer.id).await?;
78			for column in columns {
79				result.push(ColumnInfo {
80					column,
81					source_id: ringbuffer.id.into(),
82					is_view: false,
83				});
84			}
85		}
86
87		Ok(result)
88	}
89}
90
91#[cfg(test)]
92mod tests {
93	use reifydb_core::interface::TableId;
94	use reifydb_engine::test_utils::create_test_command_transaction;
95	use reifydb_type::{Type, TypeConstraint};
96
97	use crate::{
98		CatalogStore,
99		store::column::{ColumnIndex, ColumnToCreate},
100		test_utils::ensure_test_table,
101	};
102
103	#[tokio::test]
104	async fn test_ok() {
105		let mut txn = create_test_command_transaction().await;
106		ensure_test_table(&mut txn).await;
107
108		// Create columns out of order
109		CatalogStore::create_column(
110			&mut txn,
111			TableId(1),
112			ColumnToCreate {
113				fragment: None,
114				namespace_name: "test_namespace".to_string(),
115				table: TableId(1),
116				table_name: "test_table".to_string(),
117				column: "b_col".to_string(),
118				constraint: TypeConstraint::unconstrained(Type::Int4),
119				if_not_exists: false,
120				policies: vec![],
121				index: ColumnIndex(1),
122				auto_increment: true,
123				dictionary_id: None,
124			},
125		)
126		.await
127		.unwrap();
128
129		CatalogStore::create_column(
130			&mut txn,
131			TableId(1),
132			ColumnToCreate {
133				fragment: None,
134				namespace_name: "test_namespace".to_string(),
135				table: TableId(1),
136				table_name: "test_table".to_string(),
137				column: "a_col".to_string(),
138				constraint: TypeConstraint::unconstrained(Type::Boolean),
139				if_not_exists: false,
140				policies: vec![],
141				index: ColumnIndex(0),
142				auto_increment: false,
143				dictionary_id: None,
144			},
145		)
146		.await
147		.unwrap();
148
149		let columns = CatalogStore::list_columns(&mut txn, TableId(1)).await.unwrap();
150		assert_eq!(columns.len(), 2);
151
152		assert_eq!(columns[0].name, "a_col"); // index 0
153		assert_eq!(columns[1].name, "b_col"); // index 1
154
155		assert_eq!(columns[0].index, ColumnIndex(0));
156		assert_eq!(columns[1].index, ColumnIndex(1));
157
158		assert_eq!(columns[0].auto_increment, false);
159		assert_eq!(columns[1].auto_increment, true);
160	}
161
162	#[tokio::test]
163	async fn test_empty() {
164		let mut txn = create_test_command_transaction().await;
165		ensure_test_table(&mut txn).await;
166
167		let columns = CatalogStore::list_columns(&mut txn, TableId(1)).await.unwrap();
168		assert!(columns.is_empty());
169	}
170
171	#[tokio::test]
172	async fn test_table_does_not_exist() {
173		let mut txn = create_test_command_transaction().await;
174
175		let columns = CatalogStore::list_columns(&mut txn, TableId(1)).await.unwrap();
176		assert!(columns.is_empty());
177	}
178}