Skip to main content

reifydb_routine/function/rql/
fingerprint.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use bumpalo::Bump;
5use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns};
6use reifydb_rql::{
7	ast::parse_str,
8	fingerprint::{request::fingerprint_request, statement::fingerprint_statement},
9};
10use reifydb_type::value::r#type::Type;
11
12use crate::routine::{Function, FunctionKind, Routine, RoutineInfo, context::FunctionContext, error::RoutineError};
13
14pub struct RqlFingerprint {
15	info: RoutineInfo,
16}
17
18impl Default for RqlFingerprint {
19	fn default() -> Self {
20		Self::new()
21	}
22}
23
24impl RqlFingerprint {
25	pub fn new() -> Self {
26		Self {
27			info: RoutineInfo::new("rql::fingerprint"),
28		}
29	}
30}
31
32impl<'a> Routine<FunctionContext<'a>> for RqlFingerprint {
33	fn info(&self) -> &RoutineInfo {
34		&self.info
35	}
36
37	fn return_type(&self, _input_types: &[Type]) -> Type {
38		Type::Utf8
39	}
40
41	fn execute(&self, ctx: &mut FunctionContext<'a>, args: &Columns) -> Result<Columns, RoutineError> {
42		if args.len() != 1 {
43			return Err(RoutineError::FunctionArityMismatch {
44				function: ctx.fragment.clone(),
45				expected: 1,
46				actual: args.len(),
47			});
48		}
49
50		let column = &args[0];
51		let (data, bitvec) = column.unwrap_option();
52		let row_count = data.len();
53
54		match data {
55			ColumnBuffer::Utf8 {
56				container,
57				..
58			} => {
59				let mut result_data = Vec::with_capacity(row_count);
60				let mut result_bitvec = Vec::with_capacity(row_count);
61
62				for i in 0..row_count {
63					if container.is_defined(i) {
64						let query = container.get(i).unwrap();
65						let bump = Bump::new();
66						let stmts = parse_str(&bump, query).map_err(|e| {
67							RoutineError::FunctionExecutionFailed {
68								function: ctx.fragment.clone(),
69								reason: format!("{e}"),
70							}
71						})?;
72						let fps: Vec<_> =
73							stmts.iter().map(|s| fingerprint_statement(s)).collect();
74						let req = fingerprint_request(&fps);
75						result_data.push(req.to_hex());
76						result_bitvec.push(true);
77					} else {
78						result_data.push(String::new());
79						result_bitvec.push(false);
80					}
81				}
82
83				let inner_data = ColumnBuffer::utf8_with_bitvec(result_data, result_bitvec);
84				let final_data = match bitvec {
85					Some(bv) => ColumnBuffer::Option {
86						inner: Box::new(inner_data),
87						bitvec: bv.clone(),
88					},
89					None => inner_data,
90				};
91
92				Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), final_data)]))
93			}
94			other => Err(RoutineError::FunctionInvalidArgumentType {
95				function: ctx.fragment.clone(),
96				argument_index: 0,
97				expected: vec![Type::Utf8],
98				actual: other.get_type(),
99			}),
100		}
101	}
102}
103
104impl Function for RqlFingerprint {
105	fn kinds(&self) -> &[FunctionKind] {
106		&[FunctionKind::Scalar]
107	}
108}