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

getdozer / dozer / 3966459377

pending completion
3966459377

push

github

GitHub
fix: `CONCAT()` to support multiple arguments  (#690)

58 of 58 new or added lines in 3 files covered. (100.0%)

21911 of 32779 relevant lines covered (66.84%)

36246.75 hits per line

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

71.28
/dozer-sql/src/pipeline/expression/scalar/string.rs
1
use crate::arg_str;
2

3
use crate::pipeline::errors::PipelineError;
4

5
use crate::pipeline::expression::execution::{Expression, ExpressionExecutor, ExpressionType};
6

7
use crate::pipeline::expression::arg_utils::validate_arg_type;
8
use crate::pipeline::expression::scalar::common::ScalarFunctionType;
9

10
use dozer_types::types::{Field, FieldType, Record, Schema};
11
use like::{Escape, Like};
12

×
13
pub(crate) fn validate_ucase(
2✔
14
    arg: &Expression,
2✔
15
    schema: &Schema,
2✔
16
) -> Result<ExpressionType, PipelineError> {
2✔
17
    validate_arg_type(
2✔
18
        arg,
2✔
19
        vec![FieldType::String, FieldType::Text],
2✔
20
        schema,
2✔
21
        ScalarFunctionType::Ucase,
2✔
22
        0,
2✔
23
    )
2✔
24
}
2✔
25

×
26
pub(crate) fn evaluate_ucase(
2✔
27
    schema: &Schema,
2✔
28
    arg: &Expression,
2✔
29
    record: &Record,
2✔
30
) -> Result<Field, PipelineError> {
2✔
31
    let f = arg.evaluate(record, schema)?;
2✔
32
    let v = arg_str!(f, ScalarFunctionType::Ucase, 0)?;
2✔
33
    let ret = v.to_uppercase();
2✔
34

2✔
35
    Ok(match arg.get_type(schema)?.return_type {
2✔
36
        FieldType::String => Field::String(ret),
1✔
37
        _ => Field::Text(ret),
1✔
38
    })
×
39
}
2✔
40

×
41
pub(crate) fn validate_concat(
4✔
42
    args: &[Expression],
4✔
43
    schema: &Schema,
4✔
44
) -> Result<ExpressionType, PipelineError> {
4✔
45
    let mut ret_type = FieldType::String;
4✔
46
    for exp in args {
11✔
47
        let r = validate_arg_type(
8✔
48
            exp,
8✔
49
            vec![FieldType::String, FieldType::Text],
8✔
50
            schema,
8✔
51
            ScalarFunctionType::Concat,
8✔
52
            0,
8✔
53
        )?;
8✔
54
        if matches!(r.return_type, FieldType::Text) {
7✔
55
            ret_type = FieldType::Text;
2✔
56
        }
5✔
57
    }
×
58
    Ok(ExpressionType::new(ret_type, false))
3✔
59
}
4✔
60

61
pub(crate) fn evaluate_concat(
3✔
62
    schema: &Schema,
3✔
63
    args: &[Expression],
3✔
64
    record: &Record,
3✔
65
) -> Result<Field, PipelineError> {
3✔
66
    let mut res_type = FieldType::String;
3✔
67
    let mut res_vec: Vec<String> = Vec::with_capacity(args.len());
3✔
68

69
    for e in args {
9✔
70
        if matches!(e.get_type(schema)?.return_type, FieldType::Text) {
6✔
71
            res_type = FieldType::Text;
2✔
72
        }
4✔
73
        let f = e.evaluate(record, schema)?;
6✔
74
        let val = arg_str!(f, ScalarFunctionType::Concat, 0)?;
6✔
75
        res_vec.push(val);
6✔
76
    }
×
77

×
78
    let res_str = res_vec.iter().fold(String::new(), |a, b| a + b.as_str());
6✔
79
    Ok(match res_type {
3✔
80
        FieldType::Text => Field::Text(res_str),
1✔
81
        _ => Field::String(res_str),
2✔
82
    })
83
}
3✔
84

×
85
pub(crate) fn evaluate_length(
1✔
86
    schema: &Schema,
1✔
87
    arg0: &Expression,
1✔
88
    record: &Record,
1✔
89
) -> Result<Field, PipelineError> {
1✔
90
    let f0 = arg0.evaluate(record, schema)?;
1✔
91
    let v0 = arg_str!(f0, ScalarFunctionType::Concat, 0)?;
1✔
92
    Ok(Field::UInt(v0.len() as u64))
1✔
93
}
1✔
94

×
95
#[derive(Clone, Debug, PartialEq, Eq)]
3✔
96
pub enum TrimType {
×
97
    Trailing,
×
98
    Leading,
×
99
    Both,
×
100
}
×
101

×
102
pub(crate) fn validate_trim(
13✔
103
    arg: &Expression,
13✔
104
    schema: &Schema,
13✔
105
) -> Result<ExpressionType, PipelineError> {
13✔
106
    validate_arg_type(
13✔
107
        arg,
13✔
108
        vec![FieldType::String, FieldType::Text],
13✔
109
        schema,
13✔
110
        ScalarFunctionType::Concat,
13✔
111
        0,
13✔
112
    )
13✔
113
}
13✔
114

×
115
pub(crate) fn evaluate_trim(
28✔
116
    schema: &Schema,
28✔
117
    arg: &Expression,
28✔
118
    what: &Option<Box<Expression>>,
28✔
119
    typ: &Option<TrimType>,
28✔
120
    record: &Record,
28✔
121
) -> Result<Field, PipelineError> {
28✔
122
    let arg_field = arg.evaluate(record, schema)?;
28✔
123
    let arg_value = arg_str!(arg_field, "TRIM", 0)?;
28✔
124

×
125
    let v1: Vec<_> = match what {
28✔
126
        Some(e) => {
4✔
127
            let f = e.evaluate(record, schema)?;
4✔
128
            arg_str!(f, "TRIM", 1)?.chars().collect()
4✔
129
        }
×
130
        _ => vec![' '],
24✔
131
    };
×
132

×
133
    let retval = match typ {
28✔
134
        Some(TrimType::Both) => arg_value.trim_matches::<&[char]>(&v1).to_string(),
1✔
135
        Some(TrimType::Leading) => arg_value.trim_start_matches::<&[char]>(&v1).to_string(),
1✔
136
        Some(TrimType::Trailing) => arg_value.trim_end_matches::<&[char]>(&v1).to_string(),
1✔
137
        None => arg_value.trim_matches::<&[char]>(&v1).to_string(),
25✔
138
    };
×
139

×
140
    Ok(match arg.get_type(schema)?.return_type {
28✔
141
        FieldType::String => Field::String(retval),
27✔
142
        _ => Field::Text(retval),
1✔
143
    })
144
}
28✔
145

×
146
pub(crate) fn get_like_operator_type(
×
147
    arg: &Expression,
×
148
    pattern: &Expression,
×
149
    schema: &Schema,
150
) -> Result<ExpressionType, PipelineError> {
151
    validate_arg_type(
×
152
        pattern,
×
153
        vec![FieldType::String, FieldType::Text],
×
154
        schema,
×
155
        ScalarFunctionType::Concat,
×
156
        0,
×
157
    )?;
×
158

159
    validate_arg_type(
×
160
        arg,
×
161
        vec![FieldType::String, FieldType::Text],
×
162
        schema,
×
163
        ScalarFunctionType::Concat,
×
164
        0,
×
165
    )
×
166
}
×
167

×
168
pub(crate) fn evaluate_like(
5✔
169
    schema: &Schema,
5✔
170
    arg: &Expression,
5✔
171
    pattern: &Expression,
5✔
172
    escape: Option<char>,
5✔
173
    record: &Record,
5✔
174
) -> Result<Field, PipelineError> {
5✔
175
    let arg_field = arg.evaluate(record, schema)?;
5✔
176
    let arg_value = arg_str!(arg_field, "LIKE", 0)?;
5✔
177
    let arg_string = arg_value.as_str();
5✔
178

179
    let pattern_field = pattern.evaluate(record, schema)?;
5✔
180
    let pattern_value = arg_str!(pattern_field, "LIKE", 1)?;
5✔
181
    let pattern_string = pattern_value.as_str();
5✔
182

×
183
    if let Some(escape_char) = escape {
5✔
184
        let arg_escape = &arg_string
1✔
185
            .escape(&escape_char.to_string())
1✔
186
            .map_err(|e| PipelineError::InvalidArgument(e.to_string()))?;
1✔
187
        let result = Like::<false>::like(arg_escape.as_str(), pattern_string)
1✔
188
            .map(Field::Boolean)
1✔
189
            .map_err(|e| PipelineError::InvalidArgument(e.to_string()))?;
1✔
190
        return Ok(result);
1✔
191
    }
4✔
192

×
193
    let result = Like::<false>::like(arg_string, pattern_string)
4✔
194
        .map(Field::Boolean)
4✔
195
        .map_err(|e| PipelineError::InvalidArgument(e.to_string()))?;
4✔
196
    Ok(result)
4✔
197
}
5✔
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