1use super::entity::*;
4use crate::catalog::CollectionContract;
5use reddb_file::native_store::{
6 write_native_varu32 as write_varu32, write_native_varu64 as write_varu64,
7};
8use reddb_file::{
9 embedded::{RdbFileError, RdbFileResult},
10 native_store::*,
11 native_value::{write_native_value, NativeReader},
12};
13use std::collections::HashMap;
14
15pub fn required_file_capabilities_for_contract(contract: &CollectionContract) -> u64 {
17 contract
18 .declared_columns
19 .iter()
20 .fold(0, |required, column| {
21 required
22 | reddb_types::DataType::from_sql_name(&column.data_type).map_or(0, |data_type| {
23 reddb_file::native_value::required_file_capabilities_for_data_type(data_type)
24 })
25 })
26}
27
28fn persistent_name(name: &str) -> &str {
29 super::system_collection_names::canonical_or_self(name)
30}
31
32pub fn required_file_capabilities(entity: &UnifiedEntity) -> u64 {
34 fn values<'a>(values: impl Iterator<Item = &'a reddb_types::Value>) -> u64 {
35 values.fold(0, |required, value| {
36 required | reddb_file::native_value::required_file_capabilities(value)
37 })
38 }
39
40 match &entity.data {
41 EntityData::Row(row) => row.named.as_ref().map_or_else(
42 || values(row.columns.iter()),
43 |named| values(named.values()),
44 ),
45 EntityData::Node(node) => values(node.properties.values()),
46 EntityData::Edge(edge) => values(edge.properties.values()),
47 EntityData::TimeSeries(series) => values(series.fields.values()),
48 EntityData::QueueMessage(message) => {
49 reddb_file::native_value::required_file_capabilities(&message.payload)
50 }
51 EntityData::Vector(_) => 0,
52 }
53}
54
55pub fn write_entity_binary(buf: &mut Vec<u8>, entity: &UnifiedEntity, format_version: u32) {
57 write_varu64(buf, entity.id.raw());
59
60 match &entity.kind {
62 EntityKind::TableRow { table, row_id } => {
63 let table = persistent_name(table);
64 buf.push(0);
65 write_varu32(buf, table.len() as u32);
66 buf.extend_from_slice(table.as_bytes());
67 write_varu64(buf, *row_id);
68 }
69 EntityKind::GraphNode(ref node) => {
70 buf.push(1);
71 write_varu32(buf, node.label.len() as u32);
72 buf.extend_from_slice(node.label.as_bytes());
73 write_varu32(buf, node.node_type.len() as u32);
74 buf.extend_from_slice(node.node_type.as_bytes());
75 }
76 EntityKind::GraphEdge(ref edge) => {
77 buf.push(2);
78 write_varu32(buf, edge.label.len() as u32);
79 buf.extend_from_slice(edge.label.as_bytes());
80 write_varu32(buf, edge.from_node.len() as u32);
81 buf.extend_from_slice(edge.from_node.as_bytes());
82 write_varu32(buf, edge.to_node.len() as u32);
83 buf.extend_from_slice(edge.to_node.as_bytes());
84 buf.extend_from_slice(&edge.weight.to_le_bytes());
85 }
86 EntityKind::Vector { collection } => {
87 let collection = persistent_name(collection);
88 buf.push(3);
89 write_varu32(buf, collection.len() as u32);
90 buf.extend_from_slice(collection.as_bytes());
91 }
92 EntityKind::TimeSeriesPoint(ref ts) => {
93 let series = persistent_name(&ts.series);
94 buf.push(4);
95 write_varu32(buf, series.len() as u32);
96 buf.extend_from_slice(series.as_bytes());
97 write_varu32(buf, ts.metric.len() as u32);
98 buf.extend_from_slice(ts.metric.as_bytes());
99 }
100 EntityKind::QueueMessage { queue, position } => {
101 let queue = persistent_name(queue);
102 buf.push(5);
103 write_varu32(buf, queue.len() as u32);
104 buf.extend_from_slice(queue.as_bytes());
105 write_varu64(buf, *position);
106 }
107 }
108
109 match &entity.data {
111 EntityData::Row(row) => {
112 if row.named.is_some()
113 || row
114 .schema
115 .as_ref()
116 .is_some_and(|schema| schema.len() == row.columns.len())
117 {
118 buf.push(6);
121 let mut entries: Vec<_> = if let Some(named) = &row.named {
122 named
123 .iter()
124 .map(|(name, value)| (name.as_str(), value))
125 .collect()
126 } else {
127 row.schema
128 .as_ref()
129 .expect("validated schema")
130 .iter()
131 .map(String::as_str)
132 .zip(&row.columns)
133 .collect()
134 };
135 write_varu32(buf, entries.len() as u32);
136 entries.sort_by(|a, b| a.0.cmp(b.0));
137 for (key, value) in entries {
138 write_varu32(buf, key.len() as u32);
139 buf.extend_from_slice(key.as_bytes());
140 write_native_value(buf, value);
141 }
142 } else {
143 buf.push(0);
144 write_varu32(buf, row.columns.len() as u32);
145 for col in &row.columns {
146 write_native_value(buf, col);
147 }
148 }
149 }
150 EntityData::Node(node) => {
151 buf.push(1);
152 write_varu32(buf, node.properties.len() as u32);
153 let mut entries: Vec<_> = node.properties.iter().collect();
154 entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
155 for (key, value) in entries {
156 write_varu32(buf, key.len() as u32);
157 buf.extend_from_slice(key.as_bytes());
158 write_native_value(buf, value);
159 }
160 }
161 EntityData::Edge(edge) => {
162 buf.push(2);
163 buf.extend_from_slice(&edge.weight.to_le_bytes());
164 write_varu32(buf, edge.properties.len() as u32);
165 let mut entries: Vec<_> = edge.properties.iter().collect();
166 entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
167 for (key, value) in entries {
168 write_varu32(buf, key.len() as u32);
169 buf.extend_from_slice(key.as_bytes());
170 write_native_value(buf, value);
171 }
172 }
173 EntityData::Vector(vec) => {
174 buf.push(3);
175 write_varu32(buf, vec.dense.len() as u32);
176 for f in &vec.dense {
177 buf.extend_from_slice(&f.to_le_bytes());
178 }
179 if format_version >= STORE_VERSION_V6 {
180 buf.push(u8::from(vec.content.is_some()));
181 if let Some(content) = &vec.content {
182 write_varu32(buf, content.len() as u32);
183 buf.extend_from_slice(content.as_bytes());
184 }
185 }
186 }
187 EntityData::TimeSeries(ts) => {
188 buf.push(4);
189 write_varu32(buf, ts.metric.len() as u32);
190 buf.extend_from_slice(ts.metric.as_bytes());
191 write_varu64(buf, ts.timestamp_ns);
192 buf.extend_from_slice(&ts.value.to_le_bytes());
193 if format_version >= STORE_VERSION_V11 {
194 match ts.series_id {
200 Some(series_id) => {
201 write_varu32(buf, 1);
202 write_varu64(buf, series_id);
203 }
204 None => {
205 write_varu32(buf, 0);
206 write_varu32(buf, ts.tags.len() as u32);
207 let mut tag_entries: Vec<_> = ts.tags.iter().collect();
208 tag_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
209 for (key, value) in tag_entries {
210 write_varu32(buf, key.len() as u32);
211 buf.extend_from_slice(key.as_bytes());
212 write_varu32(buf, value.len() as u32);
213 buf.extend_from_slice(value.as_bytes());
214 }
215 }
216 }
217 } else if format_version >= STORE_VERSION_V5 {
218 write_varu32(buf, ts.tags.len() as u32);
219 let mut tag_entries: Vec<_> = ts.tags.iter().collect();
220 tag_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
221 for (key, value) in tag_entries {
222 write_varu32(buf, key.len() as u32);
223 buf.extend_from_slice(key.as_bytes());
224 write_varu32(buf, value.len() as u32);
225 buf.extend_from_slice(value.as_bytes());
226 }
227 }
228 if format_version >= STORE_VERSION_V12 {
229 write_varu32(buf, ts.fields.len() as u32);
230 let mut field_entries: Vec<_> = ts.fields.iter().collect();
231 field_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
232 for (key, value) in field_entries {
233 write_varu32(buf, key.len() as u32);
234 buf.extend_from_slice(key.as_bytes());
235 write_native_value(buf, value);
236 }
237 }
238 }
239 EntityData::QueueMessage(msg) => {
240 buf.push(5);
241 write_native_value(buf, &msg.payload);
242 write_varu64(buf, msg.enqueued_at_ns);
243 write_varu32(buf, msg.attempts);
244 }
245 }
246
247 write_varu64(buf, entity.created_at);
249 write_varu64(buf, entity.updated_at);
250
251 write_varu32(buf, entity.embeddings().len() as u32);
253 for emb in entity.embeddings() {
254 write_varu32(buf, emb.name.len() as u32);
255 buf.extend_from_slice(emb.name.as_bytes());
256 write_varu32(buf, emb.vector.len() as u32);
257 for f in &emb.vector {
258 buf.extend_from_slice(&f.to_le_bytes());
259 }
260 write_varu32(buf, emb.model.len() as u32);
261 buf.extend_from_slice(emb.model.as_bytes());
262 }
263
264 write_varu32(buf, entity.cross_refs().len() as u32);
266 for cross_ref in entity.cross_refs() {
267 write_varu64(buf, cross_ref.source.raw());
268 write_varu64(buf, cross_ref.target.raw());
269 buf.push(cross_ref.ref_type.to_byte());
270 if format_version >= STORE_VERSION_V2 {
271 let target_collection = persistent_name(&cross_ref.target_collection);
272 write_varu32(buf, target_collection.len() as u32);
273 buf.extend_from_slice(target_collection.as_bytes());
274 buf.extend_from_slice(&cross_ref.weight.to_le_bytes());
275 write_varu64(buf, cross_ref.created_at);
276 }
277 }
278
279 write_varu64(buf, entity.sequence_id);
281
282 if format_version >= STORE_VERSION_V4 {
283 if let EntityData::QueueMessage(message) = &entity.data {
284 buf.push(u8::from(message.priority.is_some()));
285 if let Some(priority) = message.priority {
286 buf.extend_from_slice(&priority.to_le_bytes());
287 }
288 write_varu32(buf, message.max_attempts);
289 buf.push(u8::from(message.acked));
290 }
291 }
292
293 if format_version >= STORE_VERSION_V8 {
294 buf.push(u8::from(entity.has_explicit_logical_id()));
295 if entity.has_explicit_logical_id() {
296 write_varu64(buf, entity.logical_id().raw());
297 }
298 }
299 if format_version >= STORE_VERSION_V9 {
300 write_varu64(buf, entity.xmin);
301 write_varu64(buf, entity.xmax);
302 }
303}
304
305pub fn read_entity_binary(
308 bytes: &[u8],
309 position: &mut usize,
310 format_version: u32,
311) -> RdbFileResult<UnifiedEntity> {
312 read_entity_from_reader(&mut NativeReader::new(bytes, position), format_version)
313}
314
315pub fn read_entity_binary_with_budget(
316 bytes: &[u8],
317 position: &mut usize,
318 format_version: u32,
319 budget: &mut reddb_file::native_value::DecodeBudget,
320) -> RdbFileResult<UnifiedEntity> {
321 budget.elements::<UnifiedEntity>(1)?;
322 read_entity_from_reader(
323 &mut NativeReader::with_budget(bytes, position, budget),
324 format_version,
325 )
326}
327
328fn read_entity_from_reader(
329 reader: &mut NativeReader<'_, '_>,
330 format_version: u32,
331) -> RdbFileResult<UnifiedEntity> {
332 let id = EntityId::new(reader.variable_u64()?);
333 let kind = match reader.byte()? {
334 0 => {
335 let table = super::system_collection_names::canonical_owned(reader.string()?);
336 reader.reserve_bytes(table.len().saturating_add(2 * std::mem::size_of::<usize>()))?;
337 EntityKind::TableRow {
338 table: table.into(),
339 row_id: reader.variable_u64()?,
340 }
341 }
342 1 => {
343 reader.reserve_elements::<GraphNodeKind>(1)?;
344 EntityKind::GraphNode(Box::new(GraphNodeKind {
345 label: reader.string()?,
346 node_type: reader.string()?,
347 }))
348 }
349 2 => {
350 reader.reserve_elements::<GraphEdgeKind>(1)?;
351 EntityKind::GraphEdge(Box::new(GraphEdgeKind {
352 label: reader.string()?,
353 from_node: reader.string()?,
354 to_node: reader.string()?,
355 weight: u32::from_le_bytes(reader.fixed()?),
356 }))
357 }
358 3 => EntityKind::Vector {
359 collection: super::system_collection_names::canonical_owned(reader.string()?),
360 },
361 4 => {
362 reader.reserve_elements::<TimeSeriesPointKind>(1)?;
363 EntityKind::TimeSeriesPoint(Box::new(TimeSeriesPointKind {
364 series: super::system_collection_names::canonical_owned(reader.string()?),
365 metric: reader.string()?,
366 }))
367 }
368 5 => EntityKind::QueueMessage {
369 queue: super::system_collection_names::canonical_owned(reader.string()?),
370 position: reader.variable_u64()?,
371 },
372 tag => return Err(invalid(format!("Unknown EntityKind type: {tag}"))),
373 };
374 let mut data = match reader.byte()? {
375 0 => {
376 let count = reader.count(1)?;
377 reader.reserve_elements::<reddb_types::Value>(count)?;
378 let mut columns = Vec::with_capacity(count);
379 for _ in 0..count {
380 columns.push(reader.value()?);
381 }
382 EntityData::Row(RowData::new(columns))
383 }
384 1 => EntityData::Node(NodeData::with_properties(read_properties(reader)?)),
385 2 => {
386 let mut edge = EdgeData::new(f32::from_le_bytes(reader.fixed()?));
387 edge.properties = read_properties(reader)?;
388 EntityData::Edge(edge)
389 }
390 3 => {
391 let mut vector = VectorData::new(read_vector(reader)?);
392 if format_version >= STORE_VERSION_V6 && reader.byte()? != 0 {
393 vector.content = Some(reader.string()?);
394 }
395 EntityData::Vector(vector)
396 }
397 4 => {
398 let metric = reader.string()?;
399 let timestamp_ns = reader.variable_u64()?;
400 let value = f64::from_le_bytes(reader.fixed()?);
401 let mut tags = HashMap::new();
402 let series_id = if format_version >= STORE_VERSION_V11 && reader.variable_u32()? == 1 {
403 Some(reader.variable_u64()?)
404 } else {
405 if format_version >= STORE_VERSION_V5 {
406 let count = reader.count(2)?;
407 reader.reserve_map::<String, String>(count)?;
408 tags = HashMap::with_capacity(count);
409 for _ in 0..count {
410 tags.insert(reader.string()?, reader.string()?);
411 }
412 }
413 None
414 };
415 let fields = if format_version >= STORE_VERSION_V12 {
416 read_properties(reader)?
417 } else {
418 HashMap::new()
419 };
420 EntityData::TimeSeries(TimeSeriesData {
421 metric,
422 timestamp_ns,
423 value,
424 series_id,
425 tags,
426 fields,
427 })
428 }
429 5 => EntityData::QueueMessage(QueueMessageData {
430 payload: reader.value()?,
431 priority: None,
432 enqueued_at_ns: reader.variable_u64()?,
433 attempts: reader.variable_u32()?,
434 max_attempts: 3,
435 acked: false,
436 }),
437 6 => EntityData::Row(RowData {
438 columns: Vec::new(),
439 named: Some(read_properties(reader)?),
440 schema: None,
441 }),
442 tag => return Err(invalid(format!("Unknown EntityData type: {tag}"))),
443 };
444 let created_at = reader.variable_u64()?;
445 let updated_at = reader.variable_u64()?;
446 let count = reader.count(3)?;
447 reader.reserve_elements::<EmbeddingSlot>(count)?;
448 reader.reserve_elements::<EmbeddingSlot>(count)?;
449 let mut embeddings = Vec::with_capacity(count);
450 for _ in 0..count {
451 let name = reader.string()?;
452 let vector = read_vector(reader)?;
453 let model = reader.string()?;
454 embeddings.push(EmbeddingSlot::new(name, vector, model));
455 }
456 let count = reader.count(3)?;
457 reader.reserve_elements::<CrossRef>(count)?;
458 reader.reserve_elements::<CrossRef>(count)?;
459 let mut cross_refs = Vec::with_capacity(count);
460 for _ in 0..count {
461 let source = EntityId::new(reader.variable_u64()?);
462 let target = EntityId::new(reader.variable_u64()?);
463 let ref_type = RefType::from_byte(reader.byte()?);
464 let (collection, weight, created_at) = if format_version >= STORE_VERSION_V2 {
465 (
466 super::system_collection_names::canonical_owned(reader.string()?),
467 f32::from_le_bytes(reader.fixed()?),
468 reader.variable_u64()?,
469 )
470 } else {
471 (String::new(), 1.0, 0)
472 };
473 let mut cross_ref = CrossRef::new(source, target, collection, ref_type);
474 cross_ref.weight = weight;
475 cross_ref.created_at = created_at;
476 cross_refs.push(cross_ref);
477 }
478 let sequence_id = reader.variable_u64()?;
479 if format_version >= STORE_VERSION_V4 {
480 if let EntityData::QueueMessage(message) = &mut data {
481 if reader.remaining() != 0 {
482 message.priority = if reader.byte()? != 0 {
483 Some(i32::from_le_bytes(reader.fixed()?))
484 } else {
485 None
486 };
487 message.max_attempts = reader.variable_u32()?;
488 message.acked = reader.byte()? != 0;
489 }
490 }
491 }
492 let mut entity = UnifiedEntity::new(id, kind, data);
493 entity.created_at = created_at;
494 entity.updated_at = updated_at;
495 entity.sequence_id = sequence_id;
496 if format_version >= STORE_VERSION_V8 && reader.remaining() != 0 && reader.byte()? != 0 {
497 entity.set_logical_id(EntityId::new(reader.variable_u64()?));
498 }
499 if format_version >= STORE_VERSION_V9 && reader.remaining() != 0 {
500 entity.set_xmin(reader.variable_u64()?);
501 entity.set_xmax(reader.variable_u64()?);
502 }
503 if !embeddings.is_empty() || !cross_refs.is_empty() {
504 reader.reserve_elements::<EntityAux>(1)?;
505 entity.embeddings_mut().extend(embeddings);
506 entity.cross_refs_mut().extend(cross_refs);
507 }
508 Ok(entity)
509}
510
511fn read_properties(
512 reader: &mut NativeReader<'_, '_>,
513) -> RdbFileResult<HashMap<String, reddb_types::Value>> {
514 let count = reader.count(2)?;
515 reader.reserve_map::<String, reddb_types::Value>(count)?;
516 let mut properties = HashMap::with_capacity(count);
517 for _ in 0..count {
518 properties.insert(reader.string()?, reader.value()?);
519 }
520 Ok(properties)
521}
522
523fn read_vector(reader: &mut NativeReader<'_, '_>) -> RdbFileResult<Vec<f32>> {
524 let count = reader.count(4)?;
525 reader.reserve_elements::<f32>(count)?;
526 let mut vector = Vec::with_capacity(count);
527 for _ in 0..count {
528 vector.push(f32::from_le_bytes(reader.fixed()?));
529 }
530 Ok(vector)
531}
532
533fn invalid(error: impl std::fmt::Display) -> RdbFileError {
534 RdbFileError::InvalidOperation(error.to_string())
535}