Skip to main content

reifydb_core/interface/catalog/
series.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::value::{Value, datetime::DateTime, sumtype::SumTypeId, value_type::ValueType};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8	interface::catalog::{
9		column::Column,
10		id::{NamespaceId, SeriesId},
11		key::PrimaryKey,
12	},
13	value::column::buffer::ColumnBuffer,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
17#[serde(rename_all = "lowercase")]
18#[derive(Default)]
19pub enum TimestampPrecision {
20	#[default]
21	Millisecond = 0,
22	Microsecond = 1,
23	Nanosecond = 2,
24	Second = 3,
25}
26
27#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub enum SeriesKey {
29	DateTime {
30		column: String,
31		precision: TimestampPrecision,
32	},
33	Integer {
34		column: String,
35	},
36}
37
38impl SeriesKey {
39	pub fn column(&self) -> &str {
40		match self {
41			SeriesKey::DateTime {
42				column,
43				..
44			} => column,
45			SeriesKey::Integer {
46				column,
47			} => column,
48		}
49	}
50
51	pub fn decode(key_kind: u8, precision_raw: u8, column: String) -> Self {
52		match key_kind {
53			1 => SeriesKey::Integer {
54				column,
55			},
56			_ => {
57				let precision = match precision_raw {
58					1 => TimestampPrecision::Microsecond,
59					2 => TimestampPrecision::Nanosecond,
60					3 => TimestampPrecision::Second,
61					_ => TimestampPrecision::Millisecond,
62				};
63				SeriesKey::DateTime {
64					column,
65					precision,
66				}
67			}
68		}
69	}
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct Series {
74	pub id: SeriesId,
75	pub namespace: NamespaceId,
76	pub name: String,
77	pub columns: Vec<Column>,
78	pub tag: Option<SumTypeId>,
79	pub key: SeriesKey,
80	pub primary_key: Option<PrimaryKey>,
81	pub partition_by: Vec<String>,
82	pub underlying: bool,
83}
84
85impl Series {
86	pub fn name(&self) -> &str {
87		&self.name
88	}
89
90	pub fn key_column_type(&self) -> Option<ValueType> {
91		let key_col_name = self.key.column();
92		self.columns.iter().find(|c| c.name == key_col_name).map(|c| c.constraint.get_type())
93	}
94
95	pub fn key_to_u64(&self, value: Value) -> Option<u64> {
96		match value {
97			Value::Int1(v) => u64::try_from(v).ok(),
98			Value::Int2(v) => u64::try_from(v).ok(),
99			Value::Int4(v) => u64::try_from(v).ok(),
100			Value::Int8(v) => u64::try_from(v).ok(),
101			Value::Int16(v) => u64::try_from(v).ok(),
102			Value::Uint1(v) => Some(v as u64),
103			Value::Uint2(v) => Some(v as u64),
104			Value::Uint4(v) => Some(v as u64),
105			Value::Uint8(v) => Some(v),
106			Value::Uint16(v) => u64::try_from(v).ok(),
107			Value::DateTime(dt) => {
108				let nanos = dt.to_nanos();
109				match &self.key {
110					SeriesKey::DateTime {
111						precision,
112						..
113					} => Some(match precision {
114						TimestampPrecision::Second => nanos / 1_000_000_000,
115						TimestampPrecision::Millisecond => nanos / 1_000_000,
116						TimestampPrecision::Microsecond => nanos / 1_000,
117						TimestampPrecision::Nanosecond => nanos,
118					}),
119					_ => Some(nanos),
120				}
121			}
122			_ => None,
123		}
124	}
125
126	pub fn key_from_u64(&self, v: u64) -> Value {
127		let ty = self.key_column_type();
128		match ty.as_ref() {
129			Some(ValueType::Int1) => Value::Int1(v as i8),
130			Some(ValueType::Int2) => Value::Int2(v as i16),
131			Some(ValueType::Int4) => Value::Int4(v as i32),
132			Some(ValueType::Int8) => Value::Int8(v as i64),
133			Some(ValueType::Uint1) => Value::Uint1(v as u8),
134			Some(ValueType::Uint2) => Value::Uint2(v as u16),
135			Some(ValueType::Uint4) => Value::Uint4(v as u32),
136			Some(ValueType::Uint8) => Value::Uint8(v),
137			Some(ValueType::Uint16) => Value::Uint16(v as u128),
138			Some(ValueType::Int16) => Value::Int16(v as i128),
139			Some(ValueType::DateTime) => {
140				let nanos: u64 = match &self.key {
141					SeriesKey::DateTime {
142						precision,
143						..
144					} => match precision {
145						TimestampPrecision::Second => v * 1_000_000_000,
146						TimestampPrecision::Millisecond => v * 1_000_000,
147						TimestampPrecision::Microsecond => v * 1_000,
148						TimestampPrecision::Nanosecond => v,
149					},
150					_ => v,
151				};
152				Value::DateTime(DateTime::from_nanos(nanos))
153			}
154			_ => Value::Uint8(v),
155		}
156	}
157
158	pub fn key_column_data(&self, keys: Vec<u64>) -> ColumnBuffer {
159		let key_type = self.key_column_type();
160		match &key_type {
161			Some(ty) => {
162				let mut data = ColumnBuffer::with_capacity(ty.clone(), keys.len());
163				for k in keys {
164					data.push_value(self.key_from_u64(k));
165				}
166				data
167			}
168			None => ColumnBuffer::uint8(keys),
169		}
170	}
171
172	pub fn data_columns(&self) -> impl Iterator<Item = &Column> {
173		let key_column = self.key.column().to_string();
174		self.columns.iter().filter(move |c| c.name != key_column)
175	}
176}
177
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct SeriesMetadata {
180	pub id: SeriesId,
181	pub row_count: u64,
182	pub oldest_key: u64,
183	pub newest_key: u64,
184	pub sequence_counter: u64,
185}
186
187impl SeriesMetadata {
188	pub fn new(series_id: SeriesId) -> Self {
189		Self {
190			id: series_id,
191			row_count: 0,
192			oldest_key: 0,
193			newest_key: 0,
194			sequence_counter: 0,
195		}
196	}
197}