datafusion_physical_expr/expressions/
unknown_column.rs1use std::hash::{Hash, Hasher};
21use std::sync::Arc;
22
23use crate::PhysicalExpr;
24
25use arrow::{
26 datatypes::{DataType, Schema},
27 record_batch::RecordBatch,
28};
29use datafusion_common::{Result, internal_err};
30
31use datafusion_expr::ColumnarValue;
32
33#[derive(Debug, Clone, Eq)]
34pub struct UnKnownColumn {
35 name: String,
36}
37
38impl UnKnownColumn {
39 pub fn new(name: &str) -> Self {
41 Self {
42 name: name.to_owned(),
43 }
44 }
45
46 pub fn name(&self) -> &str {
48 &self.name
49 }
50}
51
52impl std::fmt::Display for UnKnownColumn {
53 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54 write!(f, "{}", self.name)
55 }
56}
57
58impl PhysicalExpr for UnKnownColumn {
59 fn data_type(&self, _input_schema: &Schema) -> Result<DataType> {
61 Ok(DataType::Null)
62 }
63
64 fn nullable(&self, _input_schema: &Schema) -> Result<bool> {
66 Ok(true)
67 }
68
69 fn evaluate(&self, _batch: &RecordBatch) -> Result<ColumnarValue> {
71 internal_err!("UnKnownColumn::evaluate() should not be called")
72 }
73
74 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
75 vec![]
76 }
77
78 fn with_new_children(
79 self: Arc<Self>,
80 _children: Vec<Arc<dyn PhysicalExpr>>,
81 ) -> Result<Arc<dyn PhysicalExpr>> {
82 Ok(self)
83 }
84
85 fn fmt_sql(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 std::fmt::Display::fmt(self, f)
87 }
88
89 #[cfg(feature = "proto")]
90 fn try_to_proto(
91 &self,
92 _ctx: &datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncodeCtx<'_>,
93 ) -> Result<Option<datafusion_proto_models::protobuf::PhysicalExprNode>> {
94 use datafusion_proto_models::protobuf;
95
96 Ok(Some(protobuf::PhysicalExprNode {
97 expr_id: None,
98 expr_type: Some(protobuf::physical_expr_node::ExprType::UnknownColumn(
99 protobuf::UnknownColumn {
100 name: self.name.clone(),
101 },
102 )),
103 }))
104 }
105}
106
107#[cfg(feature = "proto")]
108impl UnKnownColumn {
109 pub fn try_from_proto(
111 node: &datafusion_proto_models::protobuf::PhysicalExprNode,
112 _ctx: &datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecodeCtx<'_>,
113 ) -> Result<Arc<dyn PhysicalExpr>> {
114 use datafusion_physical_expr_common::expect_expr_variant;
115 use datafusion_proto_models::protobuf;
116
117 let unknown_col = expect_expr_variant!(
118 node,
119 protobuf::physical_expr_node::ExprType::UnknownColumn,
120 "UnKnownColumn",
121 );
122 Ok(Arc::new(UnKnownColumn::new(&unknown_col.name)))
123 }
124}
125
126impl Hash for UnKnownColumn {
127 fn hash<H: Hasher>(&self, state: &mut H) {
128 self.name.hash(state);
129 }
130}
131
132impl PartialEq for UnKnownColumn {
133 fn eq(&self, _other: &Self) -> bool {
134 false
137 }
138}
139
140#[cfg(all(test, feature = "proto"))]
142mod proto_tests {
143 use super::*;
144 use crate::proto_test_util::{StubEncoder, UnreachableDecoder, column_node};
145 use arrow::datatypes::Schema;
146 use datafusion_common::DataFusionError;
147 use datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecodeCtx;
148 use datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncodeCtx;
149 use datafusion_proto_models::protobuf::{self, physical_expr_node};
150
151 #[test]
154 fn try_to_proto_encodes_unknown_column() {
155 let expr = UnKnownColumn::new("my_col");
156 let encoder = StubEncoder::ok();
157 let ctx = PhysicalExprEncodeCtx::new(&encoder);
158
159 let node = expr
160 .try_to_proto(&ctx)
161 .unwrap()
162 .expect("UnKnownColumn should encode to Some(node)");
163
164 assert!(node.expr_id.is_none());
166
167 let protobuf::UnknownColumn { name } = match node.expr_type {
169 Some(physical_expr_node::ExprType::UnknownColumn(c)) => c,
170 other => panic!("expected UnknownColumn proto node, got {other:?}"),
171 };
172 assert_eq!(name, "my_col");
173 }
174
175 #[test]
178 fn try_from_proto_decodes_name() {
179 let node = protobuf::PhysicalExprNode {
180 expr_id: None,
181 expr_type: Some(physical_expr_node::ExprType::UnknownColumn(
182 protobuf::UnknownColumn {
183 name: "my_col".to_string(),
184 },
185 )),
186 };
187 let schema = Schema::empty();
188 let decoder = UnreachableDecoder;
190 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
191
192 let decoded = UnKnownColumn::try_from_proto(&node, &ctx).unwrap();
193 let col = decoded
194 .downcast_ref::<UnKnownColumn>()
195 .expect("decoded expr should be an UnKnownColumn");
196 assert_eq!(col.name(), "my_col");
197 }
198
199 #[test]
200 fn try_from_proto_rejects_non_unknown_column_node() {
201 let node = column_node("a");
203 let schema = Schema::empty();
204 let decoder = UnreachableDecoder;
205 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
206 let err = UnKnownColumn::try_from_proto(&node, &ctx).unwrap_err();
207 assert!(matches!(
208 err,
209 DataFusionError::Internal(ref msg)
210 if msg.contains("PhysicalExprNode is not a UnKnownColumn")
211 ));
212 }
213
214 #[test]
217 fn unknown_column_proto_roundtrip() {
218 let expr = UnKnownColumn::new("col_b");
219 let encoder = StubEncoder::ok();
220 let enc_ctx = PhysicalExprEncodeCtx::new(&encoder);
221
222 let node = expr
223 .try_to_proto(&enc_ctx)
224 .unwrap()
225 .expect("UnKnownColumn should encode to Some(node)");
226
227 let schema = Schema::empty();
228 let decoder = UnreachableDecoder;
230 let dec_ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
231
232 let decoded = UnKnownColumn::try_from_proto(&node, &dec_ctx).unwrap();
233 let col = decoded
234 .downcast_ref::<UnKnownColumn>()
235 .expect("decoded expr should be an UnKnownColumn");
236 assert_eq!(col.name(), "col_b");
237 }
238}