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

getdozer / dozer / 4324181106

pending completion
4324181106

push

github

GitHub
chore: Remove some unnecessary `clone`s (#1131)

81 of 81 new or added lines in 6 files covered. (100.0%)

28161 of 36907 relevant lines covered (76.3%)

27502.29 hits per line

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

71.39
/dozer-sql/src/pipeline/expression/builder.rs
1
use dozer_types::{
2
    ordered_float::OrderedFloat,
3
    types::{Field, FieldDefinition, Schema, SourceDefinition},
4
};
5
use sqlparser::ast::{
6
    BinaryOperator as SqlBinaryOperator, DataType, Expr as SqlExpr, Expr, Function, FunctionArg,
7
    FunctionArgExpr, Ident, TrimWhereField, UnaryOperator as SqlUnaryOperator, Value as SqlValue,
8
};
9

10
use crate::pipeline::errors::PipelineError::{
11
    InvalidArgument, InvalidExpression, InvalidNestedAggregationFunction, InvalidOperator,
12
    InvalidValue,
13
};
14
use crate::pipeline::errors::{PipelineError, SqlError};
15
use crate::pipeline::expression::aggregate::AggregateFunctionType;
16
use crate::pipeline::expression::datetime::DateTimeFunctionType;
17

18
use crate::pipeline::expression::execution::Expression;
19
use crate::pipeline::expression::execution::Expression::{
20
    DateTimeFunction, GeoFunction, ScalarFunction,
21
};
22
use crate::pipeline::expression::geo::common::GeoFunctionType;
23
use crate::pipeline::expression::operator::{BinaryOperatorType, UnaryOperatorType};
24
use crate::pipeline::expression::scalar::common::ScalarFunctionType;
25
use crate::pipeline::expression::scalar::string::TrimType;
26

27
use super::cast::CastOperatorType;
28

29
#[derive(Clone, PartialEq, Debug)]
9✔
30
pub struct ExpressionBuilder {
31
    // Must be an aggregation function
32
    pub aggregations: Vec<Expression>,
33
    pub offset: usize,
34
}
35

36
impl ExpressionBuilder {
37
    pub fn new(offset: usize) -> Self {
2,969✔
38
        Self {
2,969✔
39
            aggregations: Vec::new(),
2,969✔
40
            offset,
2,969✔
41
        }
2,969✔
42
    }
2,969✔
43

44
    pub fn from(offset: usize, aggregations: Vec<Expression>) -> Self {
1✔
45
        Self {
1✔
46
            aggregations,
1✔
47
            offset,
1✔
48
        }
1✔
49
    }
1✔
50

51
    pub fn build(
2,848✔
52
        &mut self,
2,848✔
53
        parse_aggregations: bool,
2,848✔
54
        sql_expression: &SqlExpr,
2,848✔
55
        schema: &Schema,
2,848✔
56
    ) -> Result<Expression, PipelineError> {
2,848✔
57
        self.parse_sql_expression(parse_aggregations, sql_expression, schema)
2,848✔
58
    }
2,848✔
59

60
    pub(crate) fn parse_sql_expression(
61
        &mut self,
62
        parse_aggregations: bool,
63
        expression: &SqlExpr,
64
        schema: &Schema,
65
    ) -> Result<Expression, PipelineError> {
66
        match expression {
158✔
67
            SqlExpr::Trim {
68
                expr,
44✔
69
                trim_where,
44✔
70
                trim_what,
44✔
71
            } => self.parse_sql_trim_function(
44✔
72
                parse_aggregations,
44✔
73
                expr,
44✔
74
                trim_where,
44✔
75
                trim_what,
44✔
76
                schema,
44✔
77
            ),
44✔
78
            SqlExpr::Identifier(ident) => Self::parse_sql_column(&[ident.clone()], schema),
2,720✔
79
            SqlExpr::CompoundIdentifier(ident) => Self::parse_sql_column(ident, schema),
335✔
80
            SqlExpr::Value(SqlValue::Number(n, _)) => Self::parse_sql_number(n),
80✔
81
            SqlExpr::Value(SqlValue::Null) => Ok(Expression::Literal(Field::Null)),
×
82
            SqlExpr::Value(SqlValue::SingleQuotedString(s) | SqlValue::DoubleQuotedString(s)) => {
78✔
83
                Self::parse_sql_string(s)
78✔
84
            }
85
            SqlExpr::UnaryOp { expr, op } => {
×
86
                self.parse_sql_unary_op(parse_aggregations, op, expr, schema)
×
87
            }
88
            SqlExpr::BinaryOp { left, op, right } => {
138✔
89
                self.parse_sql_binary_op(parse_aggregations, left, op, right, schema)
138✔
90
            }
91
            SqlExpr::Nested(expr) => self.parse_sql_expression(parse_aggregations, expr, schema),
21✔
92
            SqlExpr::Function(sql_function) => {
333✔
93
                self.parse_sql_function(parse_aggregations, sql_function, schema)
333✔
94
            }
95
            SqlExpr::Like {
96
                negated,
×
97
                expr,
×
98
                pattern,
×
99
                escape_char,
×
100
            } => self.parse_sql_like_operator(
×
101
                parse_aggregations,
×
102
                negated,
×
103
                expr,
×
104
                pattern,
×
105
                escape_char,
×
106
                schema,
×
107
            ),
×
108
            SqlExpr::Cast { expr, data_type } => {
65✔
109
                self.parse_sql_cast_operator(parse_aggregations, expr, data_type, schema)
65✔
110
            }
111
            _ => Err(InvalidExpression(format!("{expression:?}"))),
×
112
        }
113
    }
3,814✔
114

115
    fn parse_sql_column(ident: &[Ident], schema: &Schema) -> Result<Expression, PipelineError> {
3,056✔
116
        let (src_field, src_table_or_alias, src_connection) = match ident.len() {
3,056✔
117
            1 => (&ident[0].value, None, None),
2,721✔
118
            2 => (&ident[1].value, Some(&ident[0].value), None),
334✔
119
            3 => (
1✔
120
                &ident[2].value,
1✔
121
                Some(&ident[1].value),
1✔
122
                Some(&ident[0].value),
1✔
123
            ),
1✔
124
            _ => {
125
                return Err(PipelineError::SqlError(SqlError::InvalidColumn(
×
126
                    ident
×
127
                        .iter()
×
128
                        .fold(String::new(), |a, b| a + "." + b.value.as_str()),
×
129
                )));
×
130
            }
131
        };
132

133
        let matching_by_field: Vec<(usize, &FieldDefinition)> = schema
3,056✔
134
            .fields
3,056✔
135
            .iter()
3,056✔
136
            .enumerate()
3,056✔
137
            .filter(|(_idx, f)| &f.name == src_field)
13,018✔
138
            .collect();
3,056✔
139

3,056✔
140
        match matching_by_field.len() {
3,056✔
141
            1 => Ok(Expression::Column {
2,900✔
142
                index: matching_by_field[0].0,
2,900✔
143
            }),
2,900✔
144
            _ => match src_table_or_alias {
156✔
145
                None => Err(PipelineError::SqlError(SqlError::InvalidColumn(
×
146
                    ident
×
147
                        .iter()
×
148
                        .fold(String::new(), |a, b| a + "." + b.value.as_str()),
×
149
                ))),
×
150
                Some(src_table_or_alias) => {
156✔
151
                    let matching_by_table_or_alias: Vec<(usize, &FieldDefinition)> =
156✔
152
                        matching_by_field
156✔
153
                            .into_iter()
156✔
154
                            .filter(|(_idx, field)| match &field.source {
301✔
155
                                SourceDefinition::Alias { name } => name == src_table_or_alias,
240✔
156
                                SourceDefinition::Table {
157
                                    name,
60✔
158
                                    connection: _,
60✔
159
                                } => name == src_table_or_alias,
60✔
160
                                _ => false,
×
161
                            })
301✔
162
                            .collect();
156✔
163

156✔
164
                    match matching_by_table_or_alias.len() {
156✔
165
                        1 => Ok(Expression::Column {
150✔
166
                            index: matching_by_table_or_alias[0].0,
150✔
167
                        }),
150✔
168
                        _ => match src_connection {
6✔
169
                            None => Err(PipelineError::SqlError(SqlError::InvalidColumn(
×
170
                                ident
×
171
                                    .iter()
×
172
                                    .fold(String::new(), |a, b| a + "." + b.value.as_str()),
×
173
                            ))),
×
174
                            Some(src_connection) => {
6✔
175
                                let matching_by_connection: Vec<(usize, &FieldDefinition)> =
6✔
176
                                    matching_by_table_or_alias
6✔
177
                                        .into_iter()
6✔
178
                                        .filter(|(_idx, field)| match &field.source {
6✔
179
                                            SourceDefinition::Table {
180
                                                name: _,
181
                                                connection,
×
182
                                            } => connection == src_connection,
×
183
                                            _ => false,
×
184
                                        })
6✔
185
                                        .collect();
6✔
186

6✔
187
                                match matching_by_connection.len() {
6✔
188
                                    1 => Ok(Expression::Column {
×
189
                                        index: matching_by_connection[0].0,
×
190
                                    }),
×
191
                                    _ => Err(PipelineError::SqlError(SqlError::InvalidColumn(
×
192
                                        ident
×
193
                                            .iter()
×
194
                                            .fold(String::new(), |a, b| a + "." + b.value.as_str()),
×
195
                                    ))),
×
196
                                }
197
                            }
198
                        },
199
                    }
200
                }
201
            },
202
        }
203
    }
3,050✔
204

205
    fn parse_sql_trim_function(
44✔
206
        &mut self,
44✔
207
        parse_aggregations: bool,
44✔
208
        expr: &Expr,
44✔
209
        trim_where: &Option<TrimWhereField>,
44✔
210
        trim_what: &Option<Box<Expr>>,
44✔
211
        schema: &Schema,
44✔
212
    ) -> Result<Expression, PipelineError> {
44✔
213
        let arg = Box::new(self.parse_sql_expression(parse_aggregations, expr, schema)?);
44✔
214
        let what = match trim_what {
44✔
215
            Some(e) => Some(Box::new(self.parse_sql_expression(
8✔
216
                parse_aggregations,
8✔
217
                e,
8✔
218
                schema,
8✔
219
            )?)),
8✔
220
            _ => None,
36✔
221
        };
222
        let typ = trim_where.as_ref().map(|e| match e {
44✔
223
            TrimWhereField::Both => TrimType::Both,
2✔
224
            TrimWhereField::Leading => TrimType::Leading,
2✔
225
            TrimWhereField::Trailing => TrimType::Trailing,
2✔
226
        });
44✔
227
        Ok(Expression::Trim { arg, what, typ })
44✔
228
    }
44✔
229

230
    fn parse_sql_function(
333✔
231
        &mut self,
333✔
232
        parse_aggregations: bool,
333✔
233
        sql_function: &Function,
333✔
234
        schema: &Schema,
333✔
235
    ) -> Result<Expression, PipelineError> {
333✔
236
        let function_name = sql_function.name.to_string().to_lowercase();
333✔
237

333✔
238
        #[cfg(feature = "python")]
333✔
239
        if function_name.starts_with("py_") {
333✔
240
            // The function is from python udf.
241
            let udf_name = function_name.strip_prefix("py_").unwrap();
30✔
242
            return self.parse_python_udf(udf_name, sql_function, schema);
30✔
243
        }
303✔
244

303✔
245
        match (
303✔
246
            AggregateFunctionType::new(function_name.as_str()),
303✔
247
            parse_aggregations,
303✔
248
        ) {
303✔
249
            (Ok(aggr), true) => {
252✔
250
                let mut arg_expr: Vec<Expression> = Vec::new();
252✔
251
                for arg in &sql_function.args {
507✔
252
                    let aggregation = self.parse_sql_function_arg(true, arg, schema)?;
255✔
253
                    arg_expr.push(aggregation);
255✔
254
                }
255
                let measure = Expression::AggregateFunction {
255✔
256
                    fun: aggr,
255✔
257
                    args: arg_expr,
255✔
258
                };
255✔
259
                let index = match self
255✔
260
                    .aggregations
255✔
261
                    .iter()
255✔
262
                    .enumerate()
255✔
263
                    .find(|e| e.1 == &measure)
255✔
264
                {
265
                    Some((index, _existing)) => index,
2✔
266
                    _ => {
267
                        self.aggregations.push(measure);
250✔
268
                        self.aggregations.len() - 1
250✔
269
                    }
270
                };
271
                Ok(Expression::Column {
252✔
272
                    index: self.offset + index,
252✔
273
                })
252✔
274
            }
275
            (Ok(_agg), false) => Err(InvalidNestedAggregationFunction(function_name)),
×
276
            (Err(_), _) => {
277
                let mut function_args: Vec<Expression> = Vec::new();
52✔
278
                for arg in &sql_function.args {
151✔
279
                    function_args.push(self.parse_sql_function_arg(
99✔
280
                        parse_aggregations,
99✔
281
                        arg,
99✔
282
                        schema,
99✔
283
                    )?);
99✔
284
                }
285

286
                match ScalarFunctionType::new(function_name.as_str()) {
52✔
287
                    Ok(sft) => Ok(ScalarFunction {
34✔
288
                        fun: sft,
34✔
289
                        args: function_args.clone(),
34✔
290
                    }),
34✔
291
                    Err(_d) => match GeoFunctionType::new(function_name.as_str()) {
18✔
292
                        Ok(gft) => Ok(GeoFunction {
14✔
293
                            fun: gft,
14✔
294
                            args: function_args.clone(),
14✔
295
                        }),
14✔
296
                        Err(_e) => match DateTimeFunctionType::new(function_name.as_str()) {
4✔
297
                            Ok(dft) => {
4✔
298
                                let arg = function_args
4✔
299
                                    .first()
4✔
300
                                    .ok_or(InvalidArgument(function_name))
4✔
301
                                    .unwrap();
4✔
302
                                Ok(DateTimeFunction {
4✔
303
                                    fun: dft,
4✔
304
                                    arg: Box::new(arg.clone()),
4✔
305
                                })
4✔
306
                            }
307
                            Err(_err) => Err(InvalidNestedAggregationFunction(function_name)),
×
308
                        },
309
                    },
310
                }
311
            }
312
        }
313
    }
334✔
314

315
    fn parse_sql_function_arg(
316
        &mut self,
317
        parse_aggregations: bool,
318
        argument: &FunctionArg,
319
        schema: &Schema,
320
    ) -> Result<Expression, PipelineError> {
321
        match argument {
426✔
322
            FunctionArg::Named {
323
                name: _,
324
                arg: FunctionArgExpr::Expr(arg),
×
325
            } => self.parse_sql_expression(parse_aggregations, arg, schema),
×
326
            FunctionArg::Named {
327
                name: _,
328
                arg: FunctionArgExpr::Wildcard,
329
            } => Err(InvalidArgument(format!("{argument:?}"))),
×
330
            FunctionArg::Unnamed(FunctionArgExpr::Expr(arg)) => {
426✔
331
                self.parse_sql_expression(parse_aggregations, arg, schema)
426✔
332
            }
333
            FunctionArg::Unnamed(FunctionArgExpr::Wildcard) => {
334
                Err(InvalidArgument(format!("{argument:?}")))
×
335
            }
336
            FunctionArg::Named {
337
                name: _,
338
                arg: FunctionArgExpr::QualifiedWildcard(_),
339
            } => Err(InvalidArgument(format!("{argument:?}"))),
×
340
            FunctionArg::Unnamed(FunctionArgExpr::QualifiedWildcard(_)) => {
341
                Err(InvalidArgument(format!("{argument:?}")))
×
342
            }
343
        }
344
    }
426✔
345

346
    fn parse_sql_unary_op(
×
347
        &mut self,
×
348
        parse_aggregations: bool,
×
349
        op: &SqlUnaryOperator,
×
350
        expr: &SqlExpr,
×
351
        schema: &Schema,
×
352
    ) -> Result<Expression, PipelineError> {
×
353
        let arg = Box::new(self.parse_sql_expression(parse_aggregations, expr, schema)?);
×
354
        let operator = match op {
×
355
            SqlUnaryOperator::Not => UnaryOperatorType::Not,
×
356
            SqlUnaryOperator::Plus => UnaryOperatorType::Plus,
×
357
            SqlUnaryOperator::Minus => UnaryOperatorType::Minus,
×
358
            _ => return Err(InvalidOperator(format!("{op:?}"))),
×
359
        };
360

361
        Ok(Expression::UnaryOperator { operator, arg })
×
362
    }
×
363

364
    fn parse_sql_binary_op(
138✔
365
        &mut self,
138✔
366
        parse_aggregations: bool,
138✔
367
        left: &SqlExpr,
138✔
368
        op: &SqlBinaryOperator,
138✔
369
        right: &SqlExpr,
138✔
370
        schema: &Schema,
138✔
371
    ) -> Result<Expression, PipelineError> {
138✔
372
        let left_op = self.parse_sql_expression(parse_aggregations, left, schema)?;
138✔
373
        let right_op = self.parse_sql_expression(parse_aggregations, right, schema)?;
138✔
374

375
        let operator = match op {
138✔
376
            SqlBinaryOperator::Gt => BinaryOperatorType::Gt,
31✔
377
            SqlBinaryOperator::GtEq => BinaryOperatorType::Gte,
1✔
378
            SqlBinaryOperator::Lt => BinaryOperatorType::Lt,
20✔
379
            SqlBinaryOperator::LtEq => BinaryOperatorType::Lte,
20✔
380
            SqlBinaryOperator::Eq => BinaryOperatorType::Eq,
30✔
381
            SqlBinaryOperator::NotEq => BinaryOperatorType::Ne,
×
382
            SqlBinaryOperator::Plus => BinaryOperatorType::Add,
4✔
383
            SqlBinaryOperator::Minus => BinaryOperatorType::Sub,
2✔
384
            SqlBinaryOperator::Multiply => BinaryOperatorType::Mul,
×
385
            SqlBinaryOperator::Divide => BinaryOperatorType::Div,
×
386
            SqlBinaryOperator::Modulo => BinaryOperatorType::Mod,
×
387
            SqlBinaryOperator::And => BinaryOperatorType::And,
20✔
388
            SqlBinaryOperator::Or => BinaryOperatorType::Or,
10✔
389
            _ => return Err(InvalidOperator(format!("{op:?}"))),
×
390
        };
391

392
        Ok(Expression::BinaryOperator {
138✔
393
            left: Box::new(left_op),
138✔
394
            operator,
138✔
395
            right: Box::new(right_op),
138✔
396
        })
138✔
397
    }
138✔
398

399
    fn parse_sql_number(n: &str) -> Result<Expression, PipelineError> {
80✔
400
        match n.parse::<i64>() {
80✔
401
            Ok(n) => Ok(Expression::Literal(Field::Int(n))),
80✔
402
            Err(_) => match n.parse::<f64>() {
×
403
                Ok(f) => Ok(Expression::Literal(Field::Float(OrderedFloat(f)))),
×
404
                Err(_) => Err(InvalidValue(n.to_string())),
×
405
            },
406
        }
407
    }
80✔
408

409
    fn parse_sql_like_operator(
×
410
        &mut self,
×
411
        parse_aggregations: bool,
×
412
        negated: &bool,
×
413
        expr: &Expr,
×
414
        pattern: &Expr,
×
415
        escape_char: &Option<char>,
×
416
        schema: &Schema,
×
417
    ) -> Result<Expression, PipelineError> {
×
418
        let arg = self.parse_sql_expression(parse_aggregations, expr, schema)?;
×
419
        let pattern = self.parse_sql_expression(parse_aggregations, pattern, schema)?;
×
420
        let like_expression = Expression::Like {
×
421
            arg: Box::new(arg),
×
422
            pattern: Box::new(pattern),
×
423
            escape: *escape_char,
×
424
        };
×
425
        if *negated {
×
426
            Ok(Expression::UnaryOperator {
×
427
                operator: UnaryOperatorType::Not,
×
428
                arg: Box::new(like_expression),
×
429
            })
×
430
        } else {
431
            Ok(like_expression)
×
432
        }
433
    }
×
434

435
    fn parse_sql_cast_operator(
65✔
436
        &mut self,
65✔
437
        parse_aggregations: bool,
65✔
438
        expr: &Expr,
65✔
439
        data_type: &DataType,
65✔
440
        schema: &Schema,
65✔
441
    ) -> Result<Expression, PipelineError> {
65✔
442
        let expression = self.parse_sql_expression(parse_aggregations, expr, schema)?;
65✔
443
        let cast_to = match data_type {
65✔
444
            DataType::Decimal(_) => CastOperatorType::Decimal,
×
445
            DataType::Binary(_) => CastOperatorType::Binary,
×
446
            DataType::Float(_) => CastOperatorType::Float,
10✔
447
            DataType::Int(_) => CastOperatorType::Int,
6✔
448
            DataType::Integer(_) => CastOperatorType::Int,
×
449
            DataType::UnsignedInt(_) => CastOperatorType::UInt,
×
450
            DataType::UnsignedInteger(_) => CastOperatorType::UInt,
×
451
            DataType::Boolean => CastOperatorType::Boolean,
12✔
452
            DataType::Date => CastOperatorType::Date,
×
453
            DataType::Timestamp(..) => CastOperatorType::Timestamp,
×
454
            DataType::Text => CastOperatorType::Text,
18✔
455
            DataType::String => CastOperatorType::String,
19✔
456
            DataType::Custom(name, ..) => {
×
457
                if name.to_string().to_lowercase() == "bson" {
×
458
                    CastOperatorType::Bson
×
459
                } else {
460
                    Err(PipelineError::InvalidFunction(format!(
×
461
                        "Unsupported Cast type {name}"
×
462
                    )))?
×
463
                }
464
            }
465
            _ => Err(PipelineError::InvalidFunction(format!(
×
466
                "Unsupported Cast type {data_type}"
×
467
            )))?,
×
468
        };
469
        Ok(Expression::Cast {
65✔
470
            arg: Box::new(expression),
65✔
471
            typ: cast_to,
65✔
472
        })
65✔
473
    }
65✔
474

475
    fn parse_sql_string(s: &str) -> Result<Expression, PipelineError> {
78✔
476
        Ok(Expression::Literal(Field::String(s.to_owned())))
78✔
477
    }
78✔
478

479
    pub fn fullname_from_ident(ident: &[Ident]) -> String {
110✔
480
        let mut ident_tokens = vec![];
110✔
481
        for token in ident.iter() {
110✔
482
            ident_tokens.push(token.value.clone());
110✔
483
        }
110✔
484
        ident_tokens.join(".")
110✔
485
    }
110✔
486

487
    pub(crate) fn normalize_ident(id: &Ident) -> String {
391✔
488
        match id.quote_style {
391✔
489
            Some(_) => id.value.clone(),
×
490
            None => id.value.clone(),
391✔
491
        }
492
    }
391✔
493

494
    #[cfg(feature = "python")]
495
    fn parse_python_udf(
30✔
496
        &mut self,
30✔
497
        name: &str,
30✔
498
        function: &Function,
30✔
499
        schema: &Schema,
30✔
500
    ) -> Result<Expression, PipelineError> {
30✔
501
        // First, get python function define by name.
502
        // Then, transfer python function to Expression::PythonUDF
503

504
        use dozer_types::types::FieldType;
505
        use PipelineError::InvalidQuery;
506

507
        let args = function
30✔
508
            .args
30✔
509
            .iter()
30✔
510
            .map(|argument| self.parse_sql_function_arg(false, argument, schema))
75✔
511
            .collect::<Result<Vec<_>, PipelineError>>()?;
30✔
512

513
        let last_arg = args
30✔
514
            .last()
30✔
515
            .ok_or_else(|| InvalidQuery("Can't get python udf return type".to_string()))?;
30✔
516

517
        let return_type = match last_arg {
30✔
518
            Expression::Literal(Field::String(s)) => {
30✔
519
                FieldType::try_from(s.as_str()).map_err(|e| InvalidQuery(format!("Failed to parse Python UDF return type: {e}")))?
30✔
520
            }
521
            _ => return Err(InvalidArgument("The last arg for python udf should be a string literal, which represents return type".to_string())),
×
522
        };
523

524
        Ok(Expression::PythonUDF {
30✔
525
            name: name.to_string(),
30✔
526
            args,
30✔
527
            return_type,
30✔
528
        })
30✔
529
    }
30✔
530
}
531

532
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
5,304✔
533
pub struct NameOrAlias(pub String, pub Option<String>);
534

535
pub fn extend_schema_source_def(schema: &Schema, name: &NameOrAlias) -> Schema {
1,169✔
536
    let mut output_schema = schema.clone();
1,169✔
537
    let mut fields = vec![];
1,169✔
538
    for mut field in schema.clone().fields.into_iter() {
4,500✔
539
        if let Some(alias) = &name.1 {
4,500✔
540
            field.source = SourceDefinition::Alias {
1,290✔
541
                name: alias.to_string(),
1,290✔
542
            };
1,290✔
543
        }
3,210✔
544

545
        fields.push(field);
4,500✔
546
    }
547
    output_schema.fields = fields;
1,169✔
548

1,169✔
549
    output_schema
1,169✔
550
}
1,169✔
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