1use std::time::SystemTime;
20
21use crate::model::storage::ArrowIpc;
25use crate::model::storage::codec::{Code, Id, Nested, Optional, Time, Unsigned, coded, product, variants};
26use crate::model::storage::objects::Key;
27use crate::model::storage::{ObjectKey, SchemaKey, StorageCursor};
28use crate::{Identifier, MemorySize, Result};
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
39pub struct RowSlice {
40 pub offset: u64,
41 pub limit: Option<u64>,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct ObjectSelection {
46 pub schema: Option<SchemaKey>,
47 pub key: Option<ObjectKey>,
48 pub versions: ObjectVersions,
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct RecordSelection {
53 pub schema: SchemaKey,
54 pub scope: Option<Identifier>,
55 pub item: Option<Identifier>,
56 pub source_start: Option<SystemTime>,
57 pub source_end: Option<SystemTime>,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct StreamSelection {
62 pub schema: SchemaKey,
63 pub scope: Option<Identifier>,
64 pub item: Option<Identifier>,
65 pub stream: Option<Identifier>,
66 pub source_start: Option<SystemTime>,
67 pub source_end: Option<SystemTime>,
68
69 pub after: u64,
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
73pub struct SchemaSelection {
74 pub schema: Option<SchemaKey>,
75}
76
77#[derive(Clone, Debug, PartialEq, Eq)]
78pub struct FileSelection {
79 pub scope: Option<Identifier>,
80 pub item: Option<Identifier>,
81}
82
83#[derive(Clone, Debug, PartialEq, Eq)]
84pub struct QueryCompletion {
85 pub at: StorageCursor,
86 pub rows: u64,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum ObjectVersions {
93 Latest,
94 Revisions,
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub enum Source {
99 Objects(ObjectSelection),
100 Records(RecordSelection),
101 Stream(StreamSelection),
102 Schemas(SchemaSelection),
103 Files(FileSelection),
104}
105
106#[derive(Clone, Debug)]
107pub enum QueryOutput {
108 Schema(ArrowIpc),
109 Batch(ArrowIpc),
110}
111
112impl ObjectSelection {
117 pub fn follows_revisions(&self) -> bool {
118 self.versions == ObjectVersions::Revisions
119 }
120}
121
122impl Source {
123 pub fn grant_scope(&self) -> String {
124 match self {
125 Self::Objects(selection) => {
126 selection.schema.as_ref().map_or_else(|| "objects:*".to_owned(), |schema| format!("objects:schema:{}", schema.id))
127 }
128 Self::Records(selection) => format!("records:schema:{}", selection.schema.id),
129 Self::Stream(selection) => selection
130 .stream
131 .as_ref()
132 .map_or_else(|| format!("streams:schema:{}", selection.schema.id), |stream| format!("streams:id:{stream}")),
133 Self::Schemas(selection) => {
134 selection.schema.as_ref().map_or_else(|| "schemas:*".to_owned(), |schema| format!("schemas:id:{}", schema.id))
135 }
136 Self::Files(selection) => match (&selection.scope, &selection.item) {
137 (Some(scope), _) => format!("files:scope:{scope}"),
138 (None, Some(item)) => format!("files:id:{item}"),
139 (None, None) => "files:*".to_owned(),
140 },
141 }
142 }
143
144 pub fn validate(&self) -> Result<()> {
145 match self {
146 Self::Records(selection) => window(selection.source_start, selection.source_end),
147 Self::Stream(selection) => window(selection.source_start, selection.source_end),
148 Self::Objects(_) | Self::Schemas(_) | Self::Files(_) => Ok(()),
149 }
150 }
151}
152
153product!(RowSlice { offset: Unsigned => 1, limit: Optional<Unsigned> => 2 });
154product!(ObjectSelection {
155 schema: Optional<Nested<SchemaKey>> => 1,
156 key: Optional<Key> => 2,
157 versions: Code<ObjectVersions> => 3,
158});
159product!(RecordSelection {
160 schema: Nested<SchemaKey> => 1,
161 scope: Optional<Id> => 2,
162 item: Optional<Id> => 3,
163 source_start: Optional<Time> => 4,
164 source_end: Optional<Time> => 5,
165});
166product!(StreamSelection {
167 schema: Nested<SchemaKey> => 1,
168 scope: Optional<Id> => 2,
169 item: Optional<Id> => 3,
170 stream: Optional<Id> => 4,
171 source_start: Optional<Time> => 5,
172 source_end: Optional<Time> => 6,
173 after: Unsigned => 7,
174});
175product!(SchemaSelection { schema: Optional<Nested<SchemaKey>> => 1 });
176product!(FileSelection { scope: Optional<Id> => 1, item: Optional<Id> => 2 });
177product!(QueryCompletion { at: Nested<StorageCursor> => 1, rows: Unsigned => 2 });
178coded!(ObjectVersions { Latest, Revisions });
179variants!(Source {
180 0, 1, "objects": Self::Objects(value) => { value: Nested<ObjectSelection> => 1 } => Self::Objects(value);
181 1, 2, "records": Self::Records(value) => { value: Nested<RecordSelection> => 1 } => Self::Records(value);
182 2, 3, "stream": Self::Stream(value) => { value: Nested<StreamSelection> => 1 } => Self::Stream(value);
183 3, 4, "schemas": Self::Schemas(value) => { value: Nested<SchemaSelection> => 1 } => Self::Schemas(value);
184 4, 5, "files": Self::Files(value) => { value: Nested<FileSelection> => 1 } => Self::Files(value);
185});
186variants!(QueryOutput {
187 0, 1, "schema": Self::Schema(value) => { value: Nested<ArrowIpc> => 1 } => Self::Schema(value);
188 1, 2, "batch": Self::Batch(value) => { value: Nested<ArrowIpc> => 1 } => Self::Batch(value);
189});
190
191macro_rules! memory {
192 ($ty:ty, $($field:ident),+ $(,)?) => {
193 impl MemorySize for $ty {
194 fn memory_size(&self) -> usize {
195 std::mem::size_of::<Self>()$(.saturating_add(self.$field.memory_size()
196 .saturating_sub(std::mem::size_of_val(&self.$field))))+
197 }
198 }
199 };
200}
201memory!(RowSlice, offset, limit);
202memory!(ObjectSelection, schema, key);
203memory!(RecordSelection, schema, scope, item);
204memory!(StreamSelection, schema, scope, item, stream);
205memory!(SchemaSelection, schema);
206memory!(FileSelection, scope, item);
207memory!(QueryCompletion, at);
208impl MemorySize for Source {
209 fn memory_size(&self) -> usize {
210 std::mem::size_of::<Self>()
211 + match self {
212 Self::Objects(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
213 Self::Records(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
214 Self::Stream(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
215 Self::Schemas(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
216 Self::Files(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
217 }
218 }
219}
220impl MemorySize for QueryOutput {
221 fn memory_size(&self) -> usize {
222 std::mem::size_of::<Self>()
223 + match self {
224 Self::Schema(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
225 Self::Batch(value) => value.memory_size().saturating_sub(std::mem::size_of_val(value)),
226 }
227 }
228}
229
230fn window(start: Option<SystemTime>, end: Option<SystemTime>) -> Result<()> {
235 match (start, end) {
236 (None, None) => Ok(()),
237 (Some(start), Some(end)) if start < end => Ok(()),
238 _ => Err(crate::model::storage::query::transform::invalid("source window requires increasing start and end")),
239 }
240}
241
242