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::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 underlying: bool,
82}
83
84impl Series {
85	pub fn name(&self) -> &str {
86		&self.name
87	}
88
89	pub fn key_column_type(&self) -> Option<Type> {
90		let key_col_name = self.key.column();
91		self.columns.iter().find(|c| c.name == key_col_name).map(|c| c.constraint.get_type())
92	}
93
94	pub fn key_to_u64(&self, value: Value) -> Option<u64> {
95		match value {
96			Value::Int1(v) => u64::try_from(v).ok(),
97			Value::Int2(v) => u64::try_from(v).ok(),
98			Value::Int4(v) => u64::try_from(v).ok(),
99			Value::Int8(v) => u64::try_from(v).ok(),
100			Value::Int16(v) => u64::try_from(v).ok(),
101			Value::Uint1(v) => Some(v as u64),
102			Value::Uint2(v) => Some(v as u64),
103			Value::Uint4(v) => Some(v as u64),
104			Value::Uint8(v) => Some(v),
105			Value::Uint16(v) => u64::try_from(v).ok(),
106			Value::DateTime(dt) => {
107				let nanos = dt.to_nanos();
108				match &self.key {
109					SeriesKey::DateTime {
110						precision,
111						..
112					} => Some(match precision {
113						TimestampPrecision::Second => nanos / 1_000_000_000,
114						TimestampPrecision::Millisecond => nanos / 1_000_000,
115						TimestampPrecision::Microsecond => nanos / 1_000,
116						TimestampPrecision::Nanosecond => nanos,
117					}),
118					_ => Some(nanos),
119				}
120			}
121			_ => None,
122		}
123	}
124
125	pub fn key_from_u64(&self, v: u64) -> Value {
126		let ty = self.key_column_type();
127		match ty.as_ref() {
128			Some(Type::Int1) => Value::Int1(v as i8),
129			Some(Type::Int2) => Value::Int2(v as i16),
130			Some(Type::Int4) => Value::Int4(v as i32),
131			Some(Type::Int8) => Value::Int8(v as i64),
132			Some(Type::Uint1) => Value::Uint1(v as u8),
133			Some(Type::Uint2) => Value::Uint2(v as u16),
134			Some(Type::Uint4) => Value::Uint4(v as u32),
135			Some(Type::Uint8) => Value::Uint8(v),
136			Some(Type::Uint16) => Value::Uint16(v as u128),
137			Some(Type::Int16) => Value::Int16(v as i128),
138			Some(Type::DateTime) => {
139				let nanos: u64 = match &self.key {
140					SeriesKey::DateTime {
141						precision,
142						..
143					} => match precision {
144						TimestampPrecision::Second => v * 1_000_000_000,
145						TimestampPrecision::Millisecond => v * 1_000_000,
146						TimestampPrecision::Microsecond => v * 1_000,
147						TimestampPrecision::Nanosecond => v,
148					},
149					_ => v,
150				};
151				Value::DateTime(DateTime::from_nanos(nanos))
152			}
153			_ => Value::Uint8(v),
154		}
155	}
156
157	pub fn key_column_data(&self, keys: Vec<u64>) -> ColumnBuffer {
158		let key_type = self.key_column_type();
159		match &key_type {
160			Some(ty) => {
161				let mut data = ColumnBuffer::with_capacity(ty.clone(), keys.len());
162				for k in keys {
163					data.push_value(self.key_from_u64(k));
164				}
165				data
166			}
167			None => ColumnBuffer::uint8(keys),
168		}
169	}
170
171	pub fn data_columns(&self) -> impl Iterator<Item = &Column> {
172		let key_column = self.key.column().to_string();
173		self.columns.iter().filter(move |c| c.name != key_column)
174	}
175}
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
178pub struct SeriesMetadata {
179	pub id: SeriesId,
180	pub row_count: u64,
181	pub oldest_key: u64,
182	pub newest_key: u64,
183	pub sequence_counter: u64,
184}
185
186impl SeriesMetadata {
187	pub fn new(series_id: SeriesId) -> Self {
188		Self {
189			id: series_id,
190			row_count: 0,
191			oldest_key: 0,
192			newest_key: 0,
193			sequence_counter: 0,
194		}
195	}
196}