-
Notifications
You must be signed in to change notification settings - Fork 191
Expand file tree
/
Copy pathreplace.rs
More file actions
138 lines (127 loc) · 4.85 KB
/
Copy pathreplace.rs
File metadata and controls
138 lines (127 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors
use vortex_error::VortexExpect;
use crate::dtype::DType;
use crate::dtype::Nullability;
use crate::dtype::StructFields;
use crate::expr::Expression;
use crate::expr::col;
use crate::expr::is_not_null;
use crate::expr::mask;
use crate::expr::pack;
use crate::expr::root;
use crate::expr::traversal::NodeExt;
use crate::expr::traversal::Transformed;
use crate::expr::traversal::TraversalOrder;
/// Replaces all occurrences of `needle` in the expression `expr` with `replacement`.
pub fn replace(expr: Expression, needle: &Expression, replacement: Expression) -> Expression {
expr.transform_down(|node| {
if &node == needle {
Ok(Transformed {
value: replacement.clone(),
// If there is a match with a needle there can be no more matches in that subtree.
order: TraversalOrder::Skip,
changed: true,
})
} else {
Ok(Transformed::no(node))
}
})
.vortex_expect("ReplaceVisitor should not fail")
.into_inner()
}
/// Expand the `root` expression with a pack of the given struct fields.
pub fn replace_root_fields(expr: Expression, fields: &StructFields) -> Expression {
replace(expr, &root(), root_fields_expansion(fields))
}
/// Expand the `root` expression of a struct scope into an expression over its coordinates.
///
/// For a non-nullable struct scope this is a `pack` of every field, as in
/// [`replace_root_fields`]. For a nullable struct scope, the scope has one more coordinate
/// than it has fields — its own validity — so the expansion is the identity
///
/// ```text
/// $ == mask(pack(f1: $.f1, ..., fn: $.fn), is_not_null($))
/// ```
///
/// where the surviving `$.f` references denote the fields *without* the struct's own
/// validity applied (`pack` reassembles the values, `mask` re-applies the struct validity).
/// This keeps the validity coordinate in the expression term, so downstream analyses
/// (e.g. partitioning) can route it like any other coordinate instead of re-applying it
/// out-of-band.
pub fn replace_root_scope(expr: Expression, scope: &DType) -> Expression {
let fields = scope
.as_struct_fields_opt()
.vortex_expect("replace_root_scope requires a struct scope");
let expansion = match scope.nullability() {
Nullability::NonNullable => root_fields_expansion(fields),
Nullability::Nullable => mask(root_fields_expansion(fields), is_not_null(root())),
};
replace(expr, &root(), expansion)
}
fn root_fields_expansion(fields: &StructFields) -> Expression {
pack(
fields
.names()
.iter()
.map(|name| (name.clone(), col(name.clone()))),
Nullability::NonNullable,
)
}
#[cfg(test)]
mod test {
use super::replace;
use super::replace_root_scope;
use crate::dtype::DType;
use crate::dtype::Nullability::NonNullable;
use crate::dtype::Nullability::Nullable;
use crate::dtype::PType::I32;
use crate::dtype::StructFields;
use crate::expr::col;
use crate::expr::get_item;
use crate::expr::is_not_null;
use crate::expr::lit;
use crate::expr::mask;
use crate::expr::pack;
use crate::expr::root;
#[test]
fn test_replace_full_tree() {
let e = get_item("b", pack([("a", lit(1)), ("b", lit(2))], NonNullable));
let needle = get_item("b", pack([("a", lit(1)), ("b", lit(2))], NonNullable));
let replacement = lit(42);
let replaced_expr = replace(e, &needle, replacement.clone());
assert_eq!(&replaced_expr, &replacement);
}
#[test]
fn test_replace_leaf() {
let e = pack([("a", lit(1)), ("b", lit(2))], NonNullable);
let needle = lit(2);
let replacement = lit(42);
let replaced_expr = replace(e, &needle, replacement);
assert_eq!(replaced_expr.to_string(), "pack(a: 1i32, b: 42i32)");
}
#[test]
fn test_replace_root_scope_non_nullable() {
let dtype = DType::Struct(
StructFields::from_iter([("a", I32.into()), ("b", DType::from(I32))]),
NonNullable,
);
let expanded = replace_root_scope(root(), &dtype);
let expected = pack([("a", col("a")), ("b", col("b"))], NonNullable);
assert_eq!(&expanded, &expected);
}
#[test]
fn test_replace_root_scope_nullable() {
let dtype = DType::Struct(
StructFields::from_iter([("a", I32.into()), ("b", DType::from(I32))]),
Nullable,
);
let expanded = replace_root_scope(root(), &dtype);
// A nullable struct scope has n + 1 coordinates: its fields, and its own validity.
let expected = mask(
pack([("a", col("a")), ("b", col("b"))], NonNullable),
is_not_null(root()),
);
assert_eq!(&expanded, &expected);
}
}