• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gluesql / gluesql / 20406697944

21 Dec 2025 07:44AM UTC coverage: 98.085% (-0.02%) from 98.105%
20406697944

push

github

web-flow
Add Expr::Value variant for direct runtime value injection (#1860)

Add Expr::Value(Value) variant to enable direct runtime value injection into expressions, eliminating unnecessary conversions and simplifying the AST.

478 of 483 new or added lines in 26 files covered. (98.96%)

1 existing line in 1 file now uncovered.

39434 of 40204 relevant lines covered (98.08%)

65128.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.93
/core/src/executor/evaluate/evaluated.rs
1
use {
2
    self::convert::{cast_number_to_value, cast_text_to_value, number_to_value, text_to_value},
3
    super::{error::EvaluateError, function},
4
    crate::{
5
        ast::{DataType, TrimWhereField},
6
        data::{BigDecimalExt, Key, Value, ValueError, value::BTreeMapJsonExt},
7
        result::{Error, Result},
8
    },
9
    bigdecimal::BigDecimal,
10
    std::{
11
        borrow::Cow,
12
        collections::BTreeMap,
13
        convert::TryFrom,
14
        fmt::{Display, Formatter},
15
        ops::Range,
16
    },
17
    utils::Tribool,
18
    uuid::Uuid,
19
};
20

21
mod binary_op;
22
mod cmp;
23
mod concat;
24
pub(super) mod convert;
25
mod eq;
26
mod like;
27
mod unary_op;
28

29
#[derive(Clone, Debug, PartialEq)]
30
pub enum Evaluated<'a> {
31
    Number(Cow<'a, BigDecimal>),
32
    Text(Cow<'a, str>),
33
    StrSlice {
34
        source: Cow<'a, str>,
35
        range: Range<usize>,
36
    },
37
    Value(Cow<'a, Value>),
38
}
39

40
/// Formats the evaluated value as a string.
41
/// This is primarily intended for error message generation, not for general-purpose display.
42
impl Display for Evaluated<'_> {
43
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
9,775✔
44
        match self {
9,775✔
45
            Evaluated::Number(v) => write!(f, "{v}"),
3,676✔
46
            Evaluated::Text(v) => write!(f, "{v}"),
4,877✔
47
            Evaluated::StrSlice { source, range } => write!(f, "{}", &source[range.clone()]),
1,219✔
48
            Evaluated::Value(v) => write!(f, "{}", String::from(v.as_ref())),
3✔
49
        }
50
    }
9,775✔
51
}
52

53
impl TryFrom<Evaluated<'_>> for Value {
54
    type Error = Error;
55

56
    fn try_from(e: Evaluated<'_>) -> Result<Value> {
7,351,966✔
57
        match e {
7,351,966✔
58
            Evaluated::Number(value) => {
1,623,871✔
59
                let decimal = value.as_ref();
1,623,871✔
60

61
                decimal
1,623,871✔
62
                    .to_i64()
1,623,871✔
63
                    .map(Value::I64)
1,623,871✔
64
                    .or_else(|| decimal.to_f64().filter(|f| f.is_finite()).map(Value::F64))
1,623,871✔
65
                    .ok_or_else(|| {
1,623,871✔
66
                        EvaluateError::NumberParseFailed {
1✔
67
                            literal: decimal.to_string(),
1✔
68
                            data_type: DataType::Float,
1✔
69
                        }
1✔
70
                        .into()
1✔
71
                    })
1✔
72
            }
73
            Evaluated::Text(value) => Ok(Value::Str(value.into_owned())),
737,859✔
74
            Evaluated::StrSlice {
75
                source: s,
81,607✔
76
                range: r,
81,607✔
77
            } => Ok(Value::Str(s[r].to_owned())),
81,607✔
78
            Evaluated::Value(v) => Ok(v.into_owned()),
4,908,629✔
79
        }
80
    }
7,351,966✔
81
}
82

83
impl TryFrom<Evaluated<'_>> for Key {
84
    type Error = Error;
85

86
    fn try_from(evaluated: Evaluated<'_>) -> Result<Self> {
2,087,331✔
87
        Self::try_from(&evaluated)
2,087,331✔
88
    }
2,087,331✔
89
}
90

91
impl TryFrom<&Evaluated<'_>> for Key {
92
    type Error = Error;
93

94
    fn try_from(evaluated: &Evaluated<'_>) -> Result<Self> {
2,087,331✔
95
        match evaluated {
2,087,331✔
96
            Evaluated::Number(_) | Evaluated::Text(_) => {
97
                Value::try_from(evaluated.clone())?.try_into()
69,429✔
98
            }
99
            Evaluated::StrSlice { source, range } => Ok(Key::Str(source[range.clone()].to_owned())),
1✔
100
            Evaluated::Value(v) => v.as_ref().try_into(),
2,017,901✔
101
        }
102
    }
2,087,331✔
103
}
104

105
impl TryFrom<Evaluated<'_>> for bool {
106
    type Error = Error;
107

108
    fn try_from(evaluated: Evaluated<'_>) -> Result<bool> {
4,975,358✔
109
        let v = match evaluated {
4,975,358✔
110
            Evaluated::Value(cow) => match cow.as_ref() {
4,972,921✔
111
                Value::Bool(v) => return Ok(*v),
4,971,703✔
112
                other => String::from(other),
1,218✔
113
            },
114
            Evaluated::Number(value) => value.to_string(),
1✔
115
            Evaluated::Text(value) => value.to_string(),
1,218✔
116
            Evaluated::StrSlice { source, range } => source[range].to_owned(),
1,218✔
117
        };
118

119
        Err(EvaluateError::BooleanTypeRequired(v).into())
3,655✔
120
    }
4,975,358✔
121
}
122

123
impl TryFrom<Evaluated<'_>> for BTreeMap<String, Value> {
124
    type Error = Error;
125

126
    fn try_from(evaluated: Evaluated<'_>) -> Result<BTreeMap<String, Value>> {
20,364✔
127
        match evaluated {
20,364✔
128
            Evaluated::Text(v) => BTreeMap::parse_json_object(v.as_ref()),
16,705✔
129
            Evaluated::Number(v) => Err(EvaluateError::TextLiteralRequired(v.to_string()).into()),
1✔
130
            Evaluated::Value(cow) => match cow.into_owned() {
2,439✔
131
                Value::Str(v) => BTreeMap::parse_json_object(v.as_str()),
1✔
132
                Value::Map(v) => Ok(v),
1✔
133
                v => Err(EvaluateError::MapOrStringValueRequired(v.into()).into()),
2,437✔
134
            },
135
            Evaluated::StrSlice { source, range } => BTreeMap::parse_json_object(&source[range]),
1,219✔
136
        }
137
    }
20,364✔
138
}
139

140
pub fn exceptional_int_val_to_eval<'a>(name: String, v: &Value) -> Result<Evaluated<'a>> {
3,654✔
141
    match v {
3,654✔
142
        Value::Null => Ok(Evaluated::Value(Cow::Owned(Value::Null))),
2,436✔
143
        _ => Err(EvaluateError::FunctionRequiresIntegerValue(name).into()),
1,218✔
144
    }
145
}
3,654✔
146

147
impl From<Tribool> for Evaluated<'_> {
148
    fn from(x: Tribool) -> Self {
2,738,238✔
149
        Evaluated::Value(Cow::Owned(Value::from(x)))
2,738,238✔
150
    }
2,738,238✔
151
}
152

153
impl<'a> Evaluated<'a> {
154
    pub fn arrow<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
49,938✔
155
        let selector = Value::try_from(other.clone())?;
49,938✔
156

157
        if selector.is_null() {
49,938✔
NEW
158
            return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
×
159
        }
49,938✔
160

161
        let value_result = if let Evaluated::Value(base) = self {
49,938✔
162
            function::select_arrow_value(base.as_ref(), &selector)
47,502✔
163
        } else {
164
            let base = Value::try_from(self.clone())?;
2,436✔
165
            function::select_arrow_value(&base, &selector)
2,436✔
166
        };
167

168
        value_result.map(|v| Evaluated::Value(Cow::Owned(v)))
49,938✔
169
    }
49,938✔
170

171
    pub fn cast(self, data_type: &DataType) -> Result<Evaluated<'a>> {
202,113✔
172
        match self {
202,113✔
173
            Evaluated::Number(value) => cast_number_to_value(data_type, value.as_ref()),
64,566✔
174
            Evaluated::Text(value) => cast_text_to_value(data_type, value.as_ref()),
78,039✔
175
            Evaluated::Value(value) => value.as_ref().cast(data_type),
58,290✔
176
            Evaluated::StrSlice { source, range } => {
1,218✔
177
                Value::Str(source[range].to_owned()).cast(data_type)
1,218✔
178
            }
179
        }
180
        .map(|v| Evaluated::Value(Cow::Owned(v)))
202,113✔
181
    }
202,113✔
182

183
    pub fn ltrim(self, name: String, chars: Option<Evaluated<'_>>) -> Result<Evaluated<'a>> {
24,360✔
184
        let (source, range) = match self {
19,488✔
185
            Evaluated::Text(l) => {
9,744✔
186
                let end = l.len();
9,744✔
187
                (l, 0..end)
9,744✔
188
            }
189
            Evaluated::Value(ref cow) if cow.as_ref().is_null() => {
9,744✔
190
                return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
3,654✔
191
            }
192
            Evaluated::StrSlice { source, range } => (source, range),
3,654✔
193
            Evaluated::Value(cow) => match cow.into_owned() {
6,090✔
194
                Value::Str(v) => {
6,090✔
195
                    let end = v.len();
6,090✔
196
                    (Cow::Owned(v), 0..end)
6,090✔
197
                }
NEW
198
                _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
×
199
            },
200
            Evaluated::Number(_) => {
201
                return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
202
            }
203
        };
204

205
        let filter_chars = match chars {
19,488✔
206
            Some(expr) => match expr.try_into()? {
12,180✔
207
                Value::Str(value) => value,
9,744✔
208
                Value::Null => {
209
                    return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
1,218✔
210
                }
211
                _ => {
212
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
213
                }
214
            }
215
            .chars()
9,744✔
216
            .collect::<Vec<_>>(),
9,744✔
217
            None => vec![' '],
7,308✔
218
        };
219
        let sliced_expr = &source[range.clone()];
17,052✔
220
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
17,052✔
221

222
        //"x".trim_start_matches(['x','y','z']) => ""
223
        if matched_vec.len() == sliced_expr.len() {
17,052✔
224
            return Ok(Evaluated::StrSlice {
2,436✔
225
                source,
2,436✔
226
                range: 0..0,
2,436✔
227
            });
2,436✔
228
        }
14,616✔
229
        //"tuv".trim_start_matches(['x','y','z']) => "tuv"
230
        if matched_vec.is_empty() {
14,616✔
231
            return Ok(Evaluated::StrSlice { source, range });
3,654✔
232
        }
10,962✔
233
        //"txu".trim_start_matches(['x','y','z']) => "txu"
234
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
10,962✔
235
            return Ok(Evaluated::StrSlice { source, range });
2,436✔
236
        }
8,526✔
237
        let pivot = matched_vec
8,526✔
238
            .iter()
8,526✔
239
            .enumerate()
8,526✔
240
            .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
21,924✔
241
            .map(|(vec_idx, (_, _))| vec_idx)
8,526✔
242
            .next();
8,526✔
243

244
        let start = match pivot {
8,526✔
245
            Some(idx) => match idx {
4,872✔
246
                0 => 0,
2,436✔
247
                _ => matched_vec[idx - 1].0 + 1,
2,436✔
248
            },
249
            _ => matched_vec[matched_vec.len() - 1].0 + 1,
3,654✔
250
        };
251

252
        Ok(Evaluated::StrSlice {
8,526✔
253
            source,
8,526✔
254
            range: range.start + start..range.end,
8,526✔
255
        })
8,526✔
256
    }
24,360✔
257

258
    pub fn is_null(&self) -> bool {
14,158,209✔
259
        matches!(self, Evaluated::Value(v) if v.as_ref().is_null())
11,631,293✔
260
    }
14,158,209✔
261

262
    pub fn rtrim(self, name: String, chars: Option<Evaluated<'_>>) -> Result<Evaluated<'a>> {
29,232✔
263
        let (source, range) = match self {
24,360✔
264
            Evaluated::Text(l) => {
15,834✔
265
                let end = l.len();
15,834✔
266
                (l, 0..end)
15,834✔
267
            }
268
            Evaluated::Value(ref cow) if cow.as_ref().is_null() => {
9,744✔
269
                return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
3,654✔
270
            }
271
            Evaluated::StrSlice { source, range } => (source, range),
2,436✔
272
            Evaluated::Value(cow) => match cow.into_owned() {
6,090✔
273
                Value::Str(v) => {
6,090✔
274
                    let end = v.len();
6,090✔
275
                    (Cow::Owned(v), 0..end)
6,090✔
276
                }
NEW
277
                _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
×
278
            },
279
            Evaluated::Number(_) => {
280
                return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
281
            }
282
        };
283

284
        let filter_chars = match chars {
24,360✔
285
            Some(expr) => match expr.try_into()? {
19,488✔
286
                Value::Str(value) => value,
17,052✔
287
                Value::Null => {
288
                    return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
1,218✔
289
                }
290
                _ => {
291
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
292
                }
293
            }
294
            .chars()
17,052✔
295
            .collect::<Vec<_>>(),
17,052✔
296
            None => vec![' '],
4,872✔
297
        };
298
        let sliced_expr = &source[range.clone()];
21,924✔
299
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
21,924✔
300

301
        //"x".trim_end_matches(['x','y','z']) => ""
302
        if matched_vec.len() == sliced_expr.len() {
21,924✔
303
            return Ok(Evaluated::StrSlice {
2,436✔
304
                source,
2,436✔
305
                range: 0..0,
2,436✔
306
            });
2,436✔
307
        }
19,488✔
308
        //"tuv".trim_end_matches(['x','y','z']) => "tuv"
309
        if matched_vec.is_empty() {
19,488✔
310
            return Ok(Evaluated::StrSlice { source, range });
3,654✔
311
        }
15,834✔
312
        //"txu".trim_end_matches(['x','y','z']) => "txu"
313
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
15,834✔
314
            return Ok(Evaluated::StrSlice { source, range });
2,436✔
315
        }
13,398✔
316

317
        let pivot = matched_vec
13,398✔
318
            .iter()
13,398✔
319
            .rev()
13,398✔
320
            .enumerate()
13,398✔
321
            .skip_while(|(vec_idx, (slice_idx, _))| *vec_idx == sliced_expr.len() - slice_idx - 1)
31,668✔
322
            .map(|(vec_idx, (_, _))| vec_idx)
13,398✔
323
            .next();
13,398✔
324

325
        let end = match pivot {
13,398✔
326
            Some(idx) => match idx {
4,872✔
327
                0 => range.end,
2,436✔
328
                _ => matched_vec[matched_vec.len() - idx].0,
2,436✔
329
            },
330
            _ => matched_vec[0].0,
8,526✔
331
        };
332

333
        Ok(Evaluated::StrSlice {
13,398✔
334
            source,
13,398✔
335
            range: range.start..end,
13,398✔
336
        })
13,398✔
337
    }
29,232✔
338

339
    pub fn substr(
151,033✔
340
        self,
151,033✔
341
        name: String,
151,033✔
342
        start: Evaluated<'a>,
151,033✔
343
        count: Option<Evaluated<'a>>,
151,033✔
344
    ) -> Result<Evaluated<'a>> {
151,033✔
345
        let (source, range) = match self {
148,597✔
346
            Evaluated::Text(l) => {
69,427✔
347
                let end = l.len();
69,427✔
348
                (l, 0..end)
69,427✔
349
            }
350
            Evaluated::Value(ref cow) if cow.as_ref().is_null() => {
73,080✔
351
                return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
1,218✔
352
            }
353
            Evaluated::StrSlice { source, range } => (source, range),
7,308✔
354
            Evaluated::Value(cow) => match cow.into_owned() {
71,862✔
355
                Value::Str(v) => {
71,862✔
356
                    let end = v.len();
71,862✔
357
                    (Cow::Owned(v), 0..end)
71,862✔
358
                }
NEW
359
                _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
×
360
            },
361
            Evaluated::Number(_) => {
362
                return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
363
            }
364
        };
365

366
        let start = {
146,161✔
367
            let value = start.try_into()?;
148,597✔
368
            match value {
148,597✔
369
                Value::I64(num) => num,
146,161✔
370
                _ => return exceptional_int_val_to_eval(name, &value),
2,436✔
371
            }
372
        } - 1;
373

374
        let count = match count {
146,161✔
375
            Some(eval) => {
59,683✔
376
                let value = eval.try_into()?;
59,683✔
377
                match value {
59,683✔
378
                    Value::I64(num) => num,
58,465✔
379
                    _ => return exceptional_int_val_to_eval(name, &value),
1,218✔
380
                }
381
            }
382
            None => source.len() as i64,
86,478✔
383
        };
384

385
        let end = if count < 0 {
144,943✔
386
            return Err(EvaluateError::NegativeSubstrLenNotAllowed.into());
1,218✔
387
        } else {
388
            (range.start as i64 + start + count).clamp(0, source.len() as i64) as usize
143,725✔
389
        };
390

391
        let start = (start + range.start as i64).clamp(0, source.len() as i64) as usize;
143,725✔
392

393
        Ok(Evaluated::StrSlice {
143,725✔
394
            source,
143,725✔
395
            range: start..end,
143,725✔
396
        })
143,725✔
397
    }
151,033✔
398

399
    pub fn trim(
48,720✔
400
        self,
48,720✔
401
        name: String,
48,720✔
402
        filter_chars: Option<Evaluated<'_>>,
48,720✔
403
        trim_where_field: Option<&TrimWhereField>,
48,720✔
404
    ) -> Result<Evaluated<'a>> {
48,720✔
405
        let (source, range) = match self {
41,412✔
406
            Evaluated::Text(l) => {
12,180✔
407
                let end = l.len();
12,180✔
408
                (l, 0..end)
12,180✔
409
            }
410
            Evaluated::Value(ref cow) if cow.as_ref().is_null() => {
31,668✔
411
                return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
4,872✔
412
            }
413
            Evaluated::StrSlice { source, range } => (source, range),
2,436✔
414
            Evaluated::Value(cow) => match cow.into_owned() {
26,796✔
415
                Value::Str(v) => {
26,796✔
416
                    let end = v.len();
26,796✔
417
                    (Cow::Owned(v), 0..end)
26,796✔
418
                }
NEW
419
                _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
×
420
            },
421
            Evaluated::Number(_) => {
422
                return Err(EvaluateError::FunctionRequiresStringValue(name).into());
2,436✔
423
            }
424
        };
425

426
        let filter_chars = match filter_chars {
41,412✔
427
            Some(expr) => match expr.try_into()? {
29,232✔
428
                Value::Str(value) => value,
26,796✔
429
                Value::Null => {
430
                    return Ok(Evaluated::Value(Cow::Owned(Value::Null)));
1,218✔
431
                }
432
                _ => {
433
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,218✔
434
                }
435
            }
436
            .chars()
26,796✔
437
            .collect::<Vec<_>>(),
26,796✔
438
            None => vec![' '],
12,180✔
439
        };
440
        let sliced_expr = &source[range.clone()];
38,976✔
441
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
38,976✔
442
        //filter_chars => ['x','y','z']
443
        //"x".trim_matches(filter_chars[..]) => ""
444
        if matched_vec.len() == sliced_expr.len() {
38,976✔
445
            return Ok(Evaluated::StrSlice {
2,436✔
446
                source,
2,436✔
447
                range: 0..0,
2,436✔
448
            });
2,436✔
449
        }
36,540✔
450
        //filter_chars => ['x','y','z']
451
        //"tuv".trim_matches(filter_chars[..]) => "tuv"
452
        if matched_vec.is_empty() {
36,540✔
453
            return Ok(Evaluated::StrSlice { source, range });
4,872✔
454
        }
31,668✔
455
        //filter_chars => ['x','y','z']
456
        //"txu".trim_matches(filter_chars[..]) => "txu"
457
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
31,668✔
458
            return Ok(Evaluated::StrSlice { source, range });
1,218✔
459
        }
30,450✔
460
        match trim_where_field {
24,360✔
461
            Some(TrimWhereField::Both) => {
462
                //filter_chars => ['x','y','z']
463
                //"xyzbyxlxyz  ".trim_matches(filter_chars[..]) => "byxlxyz  "
464
                if matched_vec[0].0 == 0
8,526✔
465
                    && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1
6,090✔
466
                {
467
                    let pivot = matched_vec
2,436✔
468
                        .iter()
2,436✔
469
                        .enumerate()
2,436✔
470
                        .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
13,398✔
471
                        .map(|(vec_idx, (_, _))| vec_idx)
2,436✔
472
                        .next();
2,436✔
473

474
                    let start = match pivot {
2,436✔
475
                        Some(idx) => matched_vec[idx - 1].0 + 1,
1,218✔
476
                        _ => matched_vec[matched_vec.len() - 1].0 + 1,
1,218✔
477
                    };
478

479
                    return Ok(Evaluated::StrSlice {
2,436✔
480
                        source,
2,436✔
481
                        range: range.start + start..range.end,
2,436✔
482
                    });
2,436✔
483
                }
6,090✔
484
                //filter_chars => ['x','y','z']
485
                //"  xyzblankxyzxx".trim_matches(filter_chars[..]) => "  xyzblank"
486
                if matched_vec[0].0 != 0
6,090✔
487
                    && matched_vec[matched_vec.len() - 1].0 == sliced_expr.len() - 1
2,436✔
488
                {
489
                    let pivot = matched_vec
2,436✔
490
                        .iter()
2,436✔
491
                        .rev()
2,436✔
492
                        .enumerate()
2,436✔
493
                        .skip_while(|(vec_idx, (slice_idx, _))| {
13,398✔
494
                            *vec_idx == sliced_expr.len() - slice_idx - 1
13,398✔
495
                        })
13,398✔
496
                        .map(|(vec_idx, (_, _))| vec_idx)
2,436✔
497
                        .next();
2,436✔
498

499
                    let end = match pivot {
2,436✔
500
                        Some(idx) => matched_vec[matched_vec.len() - idx].0,
1,218✔
501
                        _ => matched_vec[0].0,
1,218✔
502
                    };
503

504
                    return Ok(Evaluated::StrSlice {
2,436✔
505
                        source,
2,436✔
506
                        range: range.start..end,
2,436✔
507
                    });
2,436✔
508
                }
3,654✔
509
                //filter_chars => ['x','y','z']
510
                //"xxbyz".trim_matches(filter_chars[..]) => "b"
511
                let pivot = matched_vec
3,654✔
512
                    .iter()
3,654✔
513
                    .enumerate()
3,654✔
514
                    .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
13,398✔
515
                    .map(|(vec_idx, (_, _))| vec_idx)
3,654✔
516
                    .next()
3,654✔
517
                    .unwrap_or(0);
3,654✔
518

519
                let trim_range = matched_vec[pivot - 1].0..(matched_vec[pivot].0 + range.start);
3,654✔
520

521
                Ok(Evaluated::StrSlice {
3,654✔
522
                    source,
3,654✔
523
                    range: range.start + trim_range.start + 1..trim_range.end,
3,654✔
524
                })
3,654✔
525
            }
526
            Some(TrimWhereField::Leading) => {
527
                let pivot = matched_vec
8,526✔
528
                    .iter()
8,526✔
529
                    .enumerate()
8,526✔
530
                    .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
29,232✔
531
                    .map(|(vec_idx, (_, _))| vec_idx)
8,526✔
532
                    .next();
8,526✔
533

534
                let start = match pivot {
8,526✔
535
                    Some(idx) => match idx {
6,090✔
536
                        0 => 0,
2,436✔
537
                        _ => matched_vec[idx - 1].0 + 1,
3,654✔
538
                    },
539
                    _ => matched_vec[matched_vec.len() - 1].0 + 1,
2,436✔
540
                };
541

542
                Ok(Evaluated::StrSlice {
8,526✔
543
                    source,
8,526✔
544
                    range: range.start + start..range.end,
8,526✔
545
                })
8,526✔
546
            }
547
            Some(TrimWhereField::Trailing) => {
548
                let pivot = matched_vec
7,308✔
549
                    .iter()
7,308✔
550
                    .rev()
7,308✔
551
                    .enumerate()
7,308✔
552
                    .skip_while(|(vec_idx, (slice_idx, _))| {
26,796✔
553
                        *vec_idx == sliced_expr.len() - slice_idx - 1
26,796✔
554
                    })
26,796✔
555
                    .map(|(vec_idx, (_, _))| vec_idx)
7,308✔
556
                    .next();
7,308✔
557

558
                let end = match pivot {
7,308✔
559
                    Some(idx) => match idx {
6,090✔
560
                        0 => range.end,
2,436✔
561
                        _ => matched_vec[matched_vec.len() - idx].0,
3,654✔
562
                    },
563
                    _ => matched_vec[0].0,
1,218✔
564
                };
565

566
                Ok(Evaluated::StrSlice {
7,308✔
567
                    source,
7,308✔
568
                    range: range.start..end,
7,308✔
569
                })
7,308✔
570
            }
571
            None => {
572
                let start = source
6,090✔
573
                    .chars()
6,090✔
574
                    .skip(range.start)
6,090✔
575
                    .enumerate()
6,090✔
576
                    .find(|(_, c)| !c.is_whitespace())
23,142✔
577
                    .map_or(0, |(idx, _)| idx + range.start);
6,090✔
578

579
                let end = source.len()
6,090✔
580
                    - source
6,090✔
581
                        .chars()
6,090✔
582
                        .rev()
6,090✔
583
                        .skip(source.len() - range.end)
6,090✔
584
                        .enumerate()
6,090✔
585
                        .find(|(_, c)| !c.is_whitespace())
20,706✔
586
                        .map_or(0, |(idx, _)| source.len() - (range.end - idx));
6,090✔
587

588
                Ok(Evaluated::StrSlice {
6,090✔
589
                    source,
6,090✔
590
                    range: start..end,
6,090✔
591
                })
6,090✔
592
            }
593
        }
594
    }
48,720✔
595

596
    pub fn try_into_value(self, data_type: &DataType, nullable: bool) -> Result<Value> {
2,200,926✔
597
        let value = match self {
2,200,926✔
598
            Evaluated::Number(value) => number_to_value(data_type, value.as_ref())?,
1,427,409✔
599
            Evaluated::Text(value) => text_to_value(data_type, value.as_ref())?,
445,353✔
600
            Evaluated::Value(value) => match (value.as_ref(), data_type) {
319,638✔
601
                (Value::Bytea(bytes), DataType::Uuid) => Uuid::from_slice(bytes)
2,436✔
602
                    .map_err(|_| ValueError::FailedToParseUUID(hex::encode(bytes)))
2,436✔
603
                    .map(|uuid| Value::Uuid(uuid.as_u128()))?,
2,436✔
604
                _ => value.into_owned(),
317,202✔
605
            },
606
            Evaluated::StrSlice {
607
                source: s,
8,526✔
608
                range: r,
8,526✔
609
            } => Value::Str(s[r].to_owned()),
8,526✔
610
        };
611

612
        value.validate_type(data_type)?;
2,157,078✔
613
        value.validate_null(nullable)?;
2,154,642✔
614

615
        Ok(value)
2,148,552✔
616
    }
2,200,926✔
617
}
618

619
#[cfg(test)]
620
mod tests {
621
    use {
622
        super::Evaluated,
623
        crate::{ast::DataType, data::Value, executor::EvaluateError},
624
        bigdecimal::BigDecimal,
625
        std::{borrow::Cow, collections::BTreeMap, str::FromStr},
626
    };
627

628
    #[test]
629
    fn try_from_evaluated_to_value() {
1✔
630
        let num = |s: &str| Evaluated::Number(Cow::Owned(BigDecimal::from_str(s).unwrap()));
3✔
631
        let test = |e, result| assert_eq!(Value::try_from(e), result);
6✔
632

633
        test(num("42"), Ok(Value::I64(42)));
1✔
634
        test(num("2.5"), Ok(Value::F64(2.5)));
1✔
635
        test(
1✔
636
            num("1e400"),
1✔
637
            Err(EvaluateError::NumberParseFailed {
1✔
638
                literal: "1e+400".to_owned(),
1✔
639
                data_type: DataType::Float,
1✔
640
            }
1✔
641
            .into()),
1✔
642
        );
1✔
643
        test(
1✔
644
            Evaluated::Text(Cow::Owned("hello".to_owned())),
1✔
645
            Ok(Value::Str("hello".to_owned())),
1✔
646
        );
1✔
647
        test(
1✔
648
            Evaluated::StrSlice {
1✔
649
                source: Cow::Owned("hello world".to_owned()),
1✔
650
                range: 0..5,
1✔
651
            },
1✔
652
            Ok(Value::Str("hello".to_owned())),
1✔
653
        );
1✔
654
        test(
1✔
655
            Evaluated::Value(Cow::Owned(Value::Bool(true))),
1✔
656
            Ok(Value::Bool(true)),
1✔
657
        );
1✔
658
    }
1✔
659

660
    #[test]
661
    fn display() {
1✔
662
        let num = |s: &str| Evaluated::Number(Cow::Owned(BigDecimal::from_str(s).unwrap()));
2✔
663
        let text = |s: &str| Evaluated::Text(Cow::Owned(s.to_owned()));
1✔
664
        let slice = |s: &'static str| Evaluated::StrSlice {
1✔
665
            source: Cow::Borrowed(s),
1✔
666
            range: 0..s.len(),
1✔
667
        };
1✔
668
        let val = |v| Evaluated::Value(Cow::Owned(v));
3✔
669

670
        assert_eq!(num("42").to_string(), "42");
1✔
671
        assert_eq!(num("3.14").to_string(), "3.14");
1✔
672
        assert_eq!(text("hello").to_string(), "hello");
1✔
673
        assert_eq!(slice("world").to_string(), "world");
1✔
674
        assert_eq!(val(Value::Bool(true)).to_string(), "TRUE");
1✔
675
        assert_eq!(val(Value::Null).to_string(), "NULL");
1✔
676
        assert_eq!(val(Value::Str("foo".to_owned())).to_string(), "foo");
1✔
677
    }
1✔
678

679
    #[test]
680
    fn try_from_evaluated_to_btreemap() {
1✔
681
        let expected = || Ok([("a".to_owned(), Value::I64(1))].into_iter().collect());
5✔
682
        let test = |e, result| assert_eq!(BTreeMap::try_from(e), result);
6✔
683

684
        test(
1✔
685
            Evaluated::Text(Cow::Owned(r#"{"a": 1}"#.to_owned())),
1✔
686
            expected(),
1✔
687
        );
1✔
688
        test(
1✔
689
            Evaluated::Number(Cow::Owned(BigDecimal::from_str("42").unwrap())),
1✔
690
            Err(EvaluateError::TextLiteralRequired("42".to_owned()).into()),
1✔
691
        );
1✔
692
        test(
1✔
693
            Evaluated::Value(Cow::Owned(Value::Str(r#"{"a": 1}"#.to_owned()))),
1✔
694
            expected(),
1✔
695
        );
1✔
696
        test(
1✔
697
            Evaluated::Value(Cow::Owned(Value::Map(expected().unwrap()))),
1✔
698
            expected(),
1✔
699
        );
1✔
700
        test(
1✔
701
            Evaluated::Value(Cow::Owned(Value::Bool(true))),
1✔
702
            Err(EvaluateError::MapOrStringValueRequired("TRUE".to_owned()).into()),
1✔
703
        );
1✔
704
        test(
1✔
705
            Evaluated::StrSlice {
1✔
706
                source: Cow::Owned(r#"{"a": 1}"#.to_owned()),
1✔
707
                range: 0..8,
1✔
708
            },
1✔
709
            expected(),
1✔
710
        );
1✔
711
    }
1✔
712
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc