Skip to main content

reifydb_core/interface/catalog/
series.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_type::value::{Value, datetime::DateTime, sumtype::SumTypeId, r#type::Type};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8	interface::catalog::{
9		column::Column,
10		id::{NamespaceId, SeriesId},
11		key::PrimaryKey,
12	},
13	value::column::data::ColumnData,
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	/// Decode a `SeriesKey` from its stored representation.
52	///
53	/// `key_kind`: 1 = Integer, otherwise DateTime.
54	/// `precision_raw`: only used for DateTime keys (0=ms, 1=us, 2=ns, 3=s).
55	pub fn decode(key_kind: u8, precision_raw: u8, column: String) -> Self {
56		match key_kind {
57			1 => SeriesKey::Integer {
58				column,
59			},
60			_ => {
61				let precision = match precision_raw {
62					1 => TimestampPrecision::Microsecond,
63					2 => TimestampPrecision::Nanosecond,
64					3 => TimestampPrecision::Second,
65					_ => TimestampPrecision::Millisecond,
66				};
67				SeriesKey::DateTime {
68					column,
69					precision,
70				}
71			}
72		}
73	}
74}
75
76#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
77pub struct Series {
78	pub id: SeriesId,
79	pub namespace: NamespaceId,
80	pub name: String,
81	pub columns: Vec<Column>,
82	pub tag: Option<SumTypeId>,
83	pub key: SeriesKey,
84	pub primary_key: Option<PrimaryKey>,
85	pub underlying: bool,
86}
87
88impl Series {
89	pub fn name(&self) -> &str {
90		&self.name
91	}
92
93	/// Returns the Type of the key column, if the key column is found in columns.
94	pub fn key_column_type(&self) -> Option<Type> {
95		let key_col_name = self.key.column();
96		self.columns.iter().find(|c| c.name == key_col_name).map(|c| c.constraint.get_type())
97	}
98
99	/// Convert a Value to u64 for series key encoding.
100	///
101	/// Handles both integer and datetime key types. For datetime keys, the nanos-since-epoch
102	/// value is divided by the precision factor to recover the original u64 key.
103	/// Negative values (pre-1970 dates, negative integers) are rejected.
104	pub fn key_to_u64(&self, value: Value) -> Option<u64> {
105		match value {
106			Value::Int1(v) => u64::try_from(v).ok(),
107			Value::Int2(v) => u64::try_from(v).ok(),
108			Value::Int4(v) => u64::try_from(v).ok(),
109			Value::Int8(v) => u64::try_from(v).ok(),
110			Value::Int16(v) => u64::try_from(v).ok(),
111			Value::Uint1(v) => Some(v as u64),
112			Value::Uint2(v) => Some(v as u64),
113			Value::Uint4(v) => Some(v as u64),
114			Value::Uint8(v) => Some(v),
115			Value::Uint16(v) => u64::try_from(v).ok(),
116			Value::DateTime(dt) => {
117				let nanos = dt.to_nanos();
118				match &self.key {
119					SeriesKey::DateTime {
120						precision,
121						..
122					} => Some(match precision {
123						TimestampPrecision::Second => nanos / 1_000_000_000,
124						TimestampPrecision::Millisecond => nanos / 1_000_000,
125						TimestampPrecision::Microsecond => nanos / 1_000,
126						TimestampPrecision::Nanosecond => nanos,
127					}),
128					_ => Some(nanos),
129				}
130			}
131			_ => None,
132		}
133	}
134
135	/// Convert a u64 key value back to the appropriate Value type for encoding.
136	pub fn key_from_u64(&self, v: u64) -> Value {
137		let ty = self.key_column_type();
138		match ty.as_ref() {
139			Some(Type::Int1) => Value::Int1(v as i8),
140			Some(Type::Int2) => Value::Int2(v as i16),
141			Some(Type::Int4) => Value::Int4(v as i32),
142			Some(Type::Int8) => Value::Int8(v as i64),
143			Some(Type::Uint1) => Value::Uint1(v as u8),
144			Some(Type::Uint2) => Value::Uint2(v as u16),
145			Some(Type::Uint4) => Value::Uint4(v as u32),
146			Some(Type::Uint8) => Value::Uint8(v),
147			Some(Type::Uint16) => Value::Uint16(v as u128),
148			Some(Type::Int16) => Value::Int16(v as i128),
149			Some(Type::DateTime) => {
150				let nanos: u64 = match &self.key {
151					SeriesKey::DateTime {
152						precision,
153						..
154					} => match precision {
155						TimestampPrecision::Second => v * 1_000_000_000,
156						TimestampPrecision::Millisecond => v * 1_000_000,
157						TimestampPrecision::Microsecond => v * 1_000,
158						TimestampPrecision::Nanosecond => v,
159					},
160					_ => v,
161				};
162				Value::DateTime(DateTime::from_nanos(nanos))
163			}
164			_ => Value::Uint8(v),
165		}
166	}
167
168	/// Build a ColumnData from u64 key values using the proper key column type.
169	pub fn key_column_data(&self, keys: Vec<u64>) -> ColumnData {
170		let key_type = self.key_column_type();
171		match &key_type {
172			Some(ty) => {
173				let mut data = ColumnData::with_capacity(ty.clone(), keys.len());
174				for k in keys {
175					data.push_value(self.key_from_u64(k));
176				}
177				data
178			}
179			None => ColumnData::uint8(keys),
180		}
181	}
182
183	/// Returns columns excluding the key column (data columns only).
184	pub fn data_columns(&self) -> impl Iterator<Item = &Column> {
185		let key_column = self.key.column().to_string();
186		self.columns.iter().filter(move |c| c.name != key_column)
187	}
188}
189
190#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
191pub struct SeriesMetadata {
192	pub id: SeriesId,
193	pub row_count: u64,
194	pub oldest_key: u64,
195	pub newest_key: u64,
196	pub sequence_counter: u64,
197}
198
199impl SeriesMetadata {
200	pub fn new(series_id: SeriesId) -> Self {
201		Self {
202			id: series_id,
203			row_count: 0,
204			oldest_key: 0,
205			newest_key: 0,
206			sequence_counter: 0,
207		}
208	}
209}