graphrecords_query/operations/string_operations/
length.rs1use super::{string_map_bare, string_map_indexed};
2use crate::{
3 Bare, BareValueDomain, Explain, Failure, IndexDomain, Indexed, Labeled, Operand, QueryResult,
4 Scalar,
5 capabilities::StringValue,
6 element::Preserving,
7 error::string::StringLengthOverflow,
8 execution::EvaluationCache,
9 operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
10 optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
11 registry::operation_manifest,
12 traits::Length,
13};
14use graphrecords_core::{GraphRecord, graphrecord::GraphRecordValue};
15
16pub(super) fn length_chars(label: &'static str, value: &str) -> QueryResult<GraphRecordValue> {
17 let length = value.chars().count();
18 let length = i64::try_from(length)
19 .map_err(|_| Failure::new(label, StringLengthOverflow::new(length)))?;
20
21 Ok(GraphRecordValue::Int(length))
22}
23
24#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
25#[operation(scope = Element)]
26#[explain(label = "Length")]
27#[plan(optimizer_hints(allows_limit_pushdown, empty = if_any))]
28pub struct LengthOperation;
29
30impl Prepare for LengthOperation {
31 type Prepared<'a> = ();
32
33 fn prepare<'a>(
34 &'a self,
35 _graphrecord: &'a GraphRecord,
36 _cache: &'a EvaluationCache<'a>,
37 ) -> QueryResult<Self::Prepared<'a>> {
38 Ok(())
39 }
40}
41
42impl<I, V> ElementKernel<Indexed<I, V>> for LengthOperation
43where
44 I: IndexDomain,
45 V: StringValue,
46{
47 type Emission = Preserving;
48 type OutShape = Indexed<I, Scalar>;
49
50 fn pipeline<'a>(
51 _graphrecord: &'a GraphRecord,
52 _prepared: Self::Prepared<'a>,
53 ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
54 Ok(string_map_indexed::<I, V, Scalar>(
55 Self::LABEL,
56 |label, value| length_chars(label, &value),
57 ))
58 }
59
60 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
61 input.with_unknown_distinct()
62 }
63}
64
65impl<V> ElementKernel<Bare<V>> for LengthOperation
66where
67 V: StringValue + BareValueDomain,
68{
69 type Emission = Preserving;
70 type OutShape = Bare<Scalar>;
71
72 fn pipeline<'a>(
73 _graphrecord: &'a GraphRecord,
74 _prepared: Self::Prepared<'a>,
75 ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
76 Ok(string_map_bare::<V, Scalar>(Self::LABEL, |label, value| {
77 length_chars(label, &value)
78 }))
79 }
80
81 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
82 input.with_unknown_distinct()
83 }
84}
85
86impl<O: Apply<LengthOperation>> Length for O {
87 type ReturnOperand = O::Output;
88
89 fn length(&self) -> Self::ReturnOperand {
90 Self::ReturnOperand::new(OperationContext::new(self.clone(), LengthOperation))
91 }
92}
93
94operation_manifest! {
95 LengthOperation {
96 method: Length::length;
97 scope: element;
98
99 kernel {
100 parameters: <I: IndexDomain, V: StringValue>;
101 input: Indexed<I, V>;
102 output: Indexed<I, Scalar>;
103 emission: Preserving;
104 }
105 kernel {
106 parameters: <V: StringValue + BareValueDomain>;
107 input: Bare<V>;
108 output: Bare<Scalar>;
109 emission: Preserving;
110 }
111 }
112}