datafusion_physical_expr/expressions/
lambda_variable.rs1use std::hash::Hash;
21use std::sync::Arc;
22
23use crate::physical_expr::PhysicalExpr;
24use arrow::datatypes::FieldRef;
25use arrow::{
26 datatypes::{DataType, Schema},
27 record_batch::RecordBatch,
28};
29
30use datafusion_common::{Result, exec_err, internal_err};
31use datafusion_expr::ColumnarValue;
32
33#[derive(Debug, Clone)]
35pub struct LambdaVariable {
36 index: usize,
37 field: FieldRef,
38}
39
40impl Eq for LambdaVariable {}
41
42impl PartialEq for LambdaVariable {
43 fn eq(&self, other: &Self) -> bool {
44 self.index == other.index && self.field == other.field
45 }
46}
47
48impl Hash for LambdaVariable {
49 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50 self.index.hash(state);
51 self.field.hash(state);
52 }
53}
54
55impl LambdaVariable {
56 pub fn new(index: usize, field: FieldRef) -> Self {
58 Self { index, field }
59 }
60
61 pub fn name(&self) -> &str {
63 self.field.name()
64 }
65
66 pub fn index(&self) -> usize {
68 self.index
69 }
70
71 pub fn field(&self) -> &FieldRef {
73 &self.field
74 }
75
76 #[cfg(feature = "proto")]
77 pub fn try_from_proto(
79 node: &datafusion_proto_models::protobuf::PhysicalExprNode,
80 _ctx: &datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecodeCtx<'_>,
81 ) -> Result<Arc<dyn PhysicalExpr>> {
82 use datafusion_physical_expr_common::{
83 expect_expr_variant, physical_expr::proto_decode::require_proto_field,
84 };
85 use datafusion_proto_models::protobuf;
86
87 let var = expect_expr_variant!(
88 node,
89 protobuf::physical_expr_node::ExprType::LambdaVariable,
90 "LambdaVariable",
91 );
92
93 Ok(Arc::new(LambdaVariable::new(
94 var.index as usize,
95 Arc::new(
96 require_proto_field(var.field.as_ref(), "LambdaVariable", "field")?
97 .try_into()?,
98 ),
99 )))
100 }
101}
102
103impl std::fmt::Display for LambdaVariable {
104 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
105 write!(f, "{}@{}", self.name(), self.index)
106 }
107}
108
109impl PhysicalExpr for LambdaVariable {
110 fn data_type(&self, _input_schema: &Schema) -> Result<DataType> {
111 Ok(self.field.data_type().clone())
112 }
113
114 fn nullable(&self, _input_schema: &Schema) -> Result<bool> {
115 Ok(self.field.is_nullable())
116 }
117
118 fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
119 if self.index >= batch.num_columns() {
120 return internal_err!(
121 "PhysicalExpr LambdaVariable references column '{}' at index {} (zero-based) but batch only has {} columns: {:?}",
122 self.name(),
123 self.index,
124 batch.num_columns(),
125 batch
126 .schema_ref()
127 .fields()
128 .iter()
129 .map(|f| f.name())
130 .collect::<Vec<_>>()
131 );
132 }
133
134 if self.field.as_ref() != batch.schema_ref().field(self.index) {
135 return exec_err!(
136 "Field of physical LambdaVariable with index {} doesn't match batch field during evaluation {} != {}",
137 self.index,
138 self.field,
139 batch.schema_ref().field(self.index)
140 );
141 }
142
143 Ok(ColumnarValue::Array(Arc::clone(batch.column(self.index))))
144 }
145
146 fn return_field(&self, _input_schema: &Schema) -> Result<FieldRef> {
147 Ok(Arc::clone(&self.field))
148 }
149
150 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
151 vec![]
152 }
153
154 fn with_new_children(
155 self: Arc<Self>,
156 _children: Vec<Arc<dyn PhysicalExpr>>,
157 ) -> Result<Arc<dyn PhysicalExpr>> {
158 Ok(self)
159 }
160
161 fn fmt_sql(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 write!(f, "{}@{}", self.name(), self.index)
163 }
164
165 #[cfg(feature = "proto")]
166 fn try_to_proto(
167 &self,
168 _ctx: &datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncodeCtx<'_>,
169 ) -> Result<Option<datafusion_proto_models::protobuf::PhysicalExprNode>> {
170 use datafusion_proto_models::protobuf;
171
172 Ok(Some(protobuf::PhysicalExprNode {
173 expr_id: None,
174 expr_type: Some(protobuf::physical_expr_node::ExprType::LambdaVariable(
175 protobuf::PhysicalLambdaVariableExprNode {
176 index: self.index() as u32,
177 field: Some(self.field().as_ref().try_into()?),
178 },
179 )),
180 }))
181 }
182}
183
184pub fn lambda_variable(name: &str, schema: &Schema) -> Result<Arc<dyn PhysicalExpr>> {
186 let index = schema.index_of(name)?;
187 let field = Arc::clone(&schema.fields()[index]);
188
189 Ok(Arc::new(LambdaVariable::new(index, field)))
190}