reifydb_routine/procedure/clock/
advance.rs1use std::sync::LazyLock;
5
6use reifydb_core::value::column::columns::Columns;
7use reifydb_runtime::context::clock::Clock;
8use reifydb_type::{
9 fragment::Fragment,
10 params::Params,
11 value::{Value, datetime::DateTime, r#type::Type},
12};
13
14use super::set::extract_millis;
15use crate::routine::{Routine, RoutineInfo, context::ProcedureContext, error::RoutineError};
16
17static INFO: LazyLock<RoutineInfo> = LazyLock::new(|| RoutineInfo::new("clock::advance"));
18
19pub struct ClockAdvanceProcedure;
23
24impl Default for ClockAdvanceProcedure {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl ClockAdvanceProcedure {
31 pub fn new() -> Self {
32 Self
33 }
34}
35
36impl<'a, 'tx> Routine<ProcedureContext<'a, 'tx>> for ClockAdvanceProcedure {
37 fn info(&self) -> &RoutineInfo {
38 &INFO
39 }
40
41 fn return_type(&self, _input_types: &[Type]) -> Type {
42 Type::DateTime
43 }
44
45 fn execute(&self, ctx: &mut ProcedureContext<'a, 'tx>, _args: &Columns) -> Result<Columns, RoutineError> {
46 let arg = match ctx.params {
47 Params::Positional(args) if args.len() == 1 => &args[0],
48 Params::Positional(args) => {
49 return Err(RoutineError::ProcedureArityMismatch {
50 procedure: Fragment::internal("clock::advance"),
51 expected: 1,
52 actual: args.len(),
53 });
54 }
55 _ => {
56 return Err(RoutineError::ProcedureArityMismatch {
57 procedure: Fragment::internal("clock::advance"),
58 expected: 1,
59 actual: 0,
60 });
61 }
62 };
63
64 match &ctx.runtime_context.clock {
65 Clock::Mock(mock) => {
66 match arg {
67 Value::Duration(dur) => {
68 if dur.get_months() == 0 && dur.get_days() == 0 {
69 let nanos = dur.get_nanos();
71 if nanos >= 0 {
72 mock.advance_nanos(nanos as u64);
73 } else {
74 let current = mock.now_nanos();
75 let abs_nanos = nanos.unsigned_abs();
76 if abs_nanos > current {
77 return Err(RoutineError::ProcedureExecutionFailed {
78 procedure: Fragment::internal("clock::advance"),
79 reason: "clock cannot be set before Unix epoch".to_string(),
80 });
81 }
82 mock.set_nanos(current - abs_nanos);
83 }
84 } else {
85 let current_nanos = mock.now_nanos();
87 let current_dt = DateTime::from_nanos(current_nanos);
88 let new_dt = current_dt.add_duration(dur)?;
89 mock.set_nanos(new_dt.to_nanos());
90 }
91 }
92 other => {
93 let millis = extract_millis(other).ok_or_else(|| {
94 RoutineError::ProcedureInvalidArgumentType {
95 procedure: Fragment::internal("clock::advance"),
96 argument_index: 0,
97 expected: EXPECTED_ADVANCE_TYPES.to_vec(),
98 actual: other.get_type(),
99 }
100 })?;
101 mock.advance_millis(millis);
102 }
103 }
104 let current_nanos = mock.now_nanos();
105 let dt = DateTime::from_nanos(current_nanos);
106 Ok(Columns::single_row([("clock", Value::DateTime(dt))]))
107 }
108 Clock::Real => Err(RoutineError::ProcedureExecutionFailed {
109 procedure: Fragment::internal("clock::advance"),
110 reason: "clock::advance can only be used with a mock clock".to_string(),
111 }),
112 }
113 }
114}
115
116const EXPECTED_ADVANCE_TYPES: &[Type] = &[
117 Type::Duration,
118 Type::Int1,
119 Type::Int2,
120 Type::Int4,
121 Type::Int8,
122 Type::Int16,
123 Type::Uint1,
124 Type::Uint2,
125 Type::Uint4,
126 Type::Uint8,
127 Type::Uint16,
128];