reifydb_catalog/store/dictionary/
create.rs1use reifydb_core::{
5 diagnostic::catalog::dictionary_already_exists,
6 interface::{CommandTransaction, DictionaryDef, DictionaryId, NamespaceId},
7 return_error,
8};
9use reifydb_type::{Fragment, Type};
10
11use crate::{CatalogStore, store::sequence::SystemSequence};
12
13#[derive(Debug, Clone)]
14pub struct DictionaryToCreate {
15 pub fragment: Option<Fragment>,
16 pub dictionary: String,
17 pub namespace: NamespaceId,
18 pub value_type: Type,
19 pub id_type: Type,
20}
21
22impl CatalogStore {
23 pub async fn create_dictionary(
24 txn: &mut impl CommandTransaction,
25 to_create: DictionaryToCreate,
26 ) -> crate::Result<DictionaryDef> {
27 let namespace_id = to_create.namespace;
28
29 if let Some(dictionary) =
31 CatalogStore::find_dictionary_by_name(txn, namespace_id, &to_create.dictionary).await?
32 {
33 let namespace = CatalogStore::get_namespace(txn, namespace_id).await?;
34 return_error!(dictionary_already_exists(
35 to_create.fragment.unwrap_or_else(|| Fragment::None),
36 &namespace.name,
37 &dictionary.name
38 ));
39 }
40
41 let dictionary_id = SystemSequence::next_dictionary_id(txn).await?;
43
44 Self::store_dictionary(txn, dictionary_id, namespace_id, &to_create).await?;
46
47 Self::link_dictionary_to_namespace(txn, namespace_id, dictionary_id, &to_create.dictionary).await?;
49
50 Self::initialize_dictionary_sequence(txn, dictionary_id).await?;
52
53 Ok(Self::get_dictionary(txn, dictionary_id).await?)
54 }
55
56 async fn store_dictionary(
57 txn: &mut impl CommandTransaction,
58 dictionary: DictionaryId,
59 namespace: NamespaceId,
60 to_create: &DictionaryToCreate,
61 ) -> crate::Result<()> {
62 use reifydb_core::key::DictionaryKey;
63
64 use crate::store::dictionary::layout::dictionary;
65
66 let mut row = dictionary::LAYOUT.allocate();
67 dictionary::LAYOUT.set_u64(&mut row, dictionary::ID, dictionary);
68 dictionary::LAYOUT.set_u64(&mut row, dictionary::NAMESPACE, namespace);
69 dictionary::LAYOUT.set_utf8(&mut row, dictionary::NAME, &to_create.dictionary);
70 dictionary::LAYOUT.set_u8(&mut row, dictionary::VALUE_TYPE, to_create.value_type.to_u8());
71 dictionary::LAYOUT.set_u8(&mut row, dictionary::ID_TYPE, to_create.id_type.to_u8());
72
73 txn.set(&DictionaryKey::encoded(dictionary), row).await?;
74
75 Ok(())
76 }
77
78 async fn link_dictionary_to_namespace(
79 txn: &mut impl CommandTransaction,
80 namespace: NamespaceId,
81 dictionary: DictionaryId,
82 name: &str,
83 ) -> crate::Result<()> {
84 use reifydb_core::key::NamespaceDictionaryKey;
85
86 use crate::store::dictionary::layout::dictionary_namespace;
87
88 let mut row = dictionary_namespace::LAYOUT.allocate();
89 dictionary_namespace::LAYOUT.set_u64(&mut row, dictionary_namespace::ID, dictionary);
90 dictionary_namespace::LAYOUT.set_utf8(&mut row, dictionary_namespace::NAME, name);
91
92 txn.set(&NamespaceDictionaryKey::encoded(namespace, dictionary), row).await?;
93
94 Ok(())
95 }
96
97 async fn initialize_dictionary_sequence(
98 txn: &mut impl CommandTransaction,
99 dictionary: DictionaryId,
100 ) -> crate::Result<()> {
101 use reifydb_core::{key::DictionarySequenceKey, util::CowVec, value::encoded::EncodedValues};
102
103 let seq_key = DictionarySequenceKey::encoded(dictionary);
106 let initial_value = 0u128.to_be_bytes().to_vec();
107
108 txn.set(&seq_key, EncodedValues(CowVec::new(initial_value))).await?;
109
110 Ok(())
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use reifydb_core::interface::{MultiVersionQueryTransaction, NamespaceDictionaryKey};
117 use reifydb_engine::test_utils::create_test_command_transaction;
118 use reifydb_type::Type;
119
120 use super::*;
121 use crate::{store::dictionary::layout::dictionary_namespace, test_utils::ensure_test_namespace};
122
123 #[tokio::test]
124 async fn test_create_simple_dictionary() {
125 let mut txn = create_test_command_transaction().await;
126 let test_namespace = ensure_test_namespace(&mut txn).await;
127
128 let to_create = DictionaryToCreate {
129 namespace: test_namespace.id,
130 dictionary: "token_mints".to_string(),
131 value_type: Type::Utf8,
132 id_type: Type::Uint2,
133 fragment: None,
134 };
135
136 let result = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
137
138 assert!(result.id.0 > 0);
139 assert_eq!(result.namespace, test_namespace.id);
140 assert_eq!(result.name, "token_mints");
141 assert_eq!(result.value_type, Type::Utf8);
142 assert_eq!(result.id_type, Type::Uint2);
143 }
144
145 #[tokio::test]
146 async fn test_create_duplicate_dictionary() {
147 let mut txn = create_test_command_transaction().await;
148 let test_namespace = ensure_test_namespace(&mut txn).await;
149
150 let to_create = DictionaryToCreate {
151 namespace: test_namespace.id,
152 dictionary: "test_dict".to_string(),
153 value_type: Type::Utf8,
154 id_type: Type::Uint4,
155 fragment: None,
156 };
157
158 let result = CatalogStore::create_dictionary(&mut txn, to_create.clone()).await.unwrap();
160 assert!(result.id.0 > 0);
161 assert_eq!(result.namespace, test_namespace.id);
162 assert_eq!(result.name, "test_dict");
163
164 let err = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap_err();
166 assert_eq!(err.diagnostic().code, "CA_006");
167 }
168
169 #[tokio::test]
170 async fn test_dictionary_linked_to_namespace() {
171 let mut txn = create_test_command_transaction().await;
172 let test_namespace = ensure_test_namespace(&mut txn).await;
173
174 let to_create1 = DictionaryToCreate {
175 namespace: test_namespace.id,
176 dictionary: "dict1".to_string(),
177 value_type: Type::Utf8,
178 id_type: Type::Uint1,
179 fragment: None,
180 };
181
182 CatalogStore::create_dictionary(&mut txn, to_create1).await.unwrap();
183
184 let to_create2 = DictionaryToCreate {
185 namespace: test_namespace.id,
186 dictionary: "dict2".to_string(),
187 value_type: Type::Uint8,
188 id_type: Type::Uint2,
189 fragment: None,
190 };
191
192 CatalogStore::create_dictionary(&mut txn, to_create2).await.unwrap();
193
194 let links = txn
196 .range(NamespaceDictionaryKey::full_scan(test_namespace.id))
197 .await
198 .unwrap()
199 .items
200 .into_iter()
201 .collect::<Vec<_>>();
202 assert_eq!(links.len(), 2);
203
204 let link = &links[0];
206 let row = &link.values;
207 let id2 = dictionary_namespace::LAYOUT.get_u64(row, dictionary_namespace::ID);
208 assert!(id2 > 0);
209 assert_eq!(dictionary_namespace::LAYOUT.get_utf8(row, dictionary_namespace::NAME), "dict2");
210
211 let link = &links[1];
213 let row = &link.values;
214 let id1 = dictionary_namespace::LAYOUT.get_u64(row, dictionary_namespace::ID);
215 assert!(id2 > id1);
216 assert_eq!(dictionary_namespace::LAYOUT.get_utf8(row, dictionary_namespace::NAME), "dict1");
217 }
218
219 #[tokio::test]
220 async fn test_create_dictionary_with_various_types() {
221 let mut txn = create_test_command_transaction().await;
222 let test_namespace = ensure_test_namespace(&mut txn).await;
223
224 let to_create = DictionaryToCreate {
226 namespace: test_namespace.id,
227 dictionary: "small_dict".to_string(),
228 value_type: Type::Utf8,
229 id_type: Type::Uint1,
230 fragment: None,
231 };
232 let result = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
233 assert_eq!(result.id_type, Type::Uint1);
234
235 let to_create = DictionaryToCreate {
237 namespace: test_namespace.id,
238 dictionary: "large_dict".to_string(),
239 value_type: Type::Blob,
240 id_type: Type::Uint8,
241 fragment: None,
242 };
243 let result = CatalogStore::create_dictionary(&mut txn, to_create).await.unwrap();
244 assert_eq!(result.id_type, Type::Uint8);
245 assert_eq!(result.value_type, Type::Blob);
246 }
247}