@@ -22,6 +22,7 @@ use std::cmp::Ordering;
2222use std:: collections:: { HashMap , HashSet } ;
2323use std:: iter:: once;
2424use std:: sync:: Arc ;
25+ use datafusion_common:: metadata:: check_metadata_with_storage_equal;
2526
2627use crate :: dml:: CopyTo ;
2728use crate :: expr:: { Alias , PlannedReplaceSelectItem , Sort as SortExpr } ;
@@ -274,28 +275,42 @@ impl LogicalPlanBuilder {
274275 for j in 0 ..n_cols {
275276 let field = schema. field ( j) ;
276277 let field_type = field. data_type ( ) ;
277- let field_nullable = field. is_nullable ( ) ;
278- let field_metadata = FieldMetadata :: new_from_field ( field) ;
278+ let field_metadata = field. metadata ( ) ;
279+ let is_target_ext = field_metadata. contains_key ( arrow_schema:: extension:: EXTENSION_TYPE_NAME_KEY ) ;
280+ let column_name = format ! ( "column {}" , j) ;
279281 for row in values. iter ( ) {
280282 let value = & row[ j] ;
281- let data_type = value. get_type ( schema) ?;
282-
283- if !data_type. equals_datatype ( field_type)
284- && !can_cast_types ( & data_type, field_type)
285- {
286- return exec_err ! (
287- "type mismatch and can't cast to got {} and {}" ,
288- data_type,
289- field_type
290- ) ;
283+ let value_type = value. get_type ( schema) ?;
284+ if value_type == DataType :: Null { continue ; }
285+
286+ if is_target_ext {
287+ let value_meta = value. metadata ( schema) ?;
288+ let value_meta_map = value_meta. to_hashmap ( ) ;
289+ let is_value_ext = value_meta_map. contains_key ( arrow_schema:: extension:: EXTENSION_TYPE_NAME_KEY ) ;
290+
291+ if is_value_ext && value_type == * field_type {
292+ check_metadata_with_storage_equal (
293+ ( & value_type, Some ( & value_meta_map) ) ,
294+ ( field_type, Some ( field_metadata) ) ,
295+ & column_name,
296+ " in VALUES list" ,
297+ ) ?;
298+ } else if !can_cast_types ( & value_type, field_type) {
299+ return plan_err ! ( "Cannot cast {} to extension type at column {}" , value_type, column_name) ;
300+ }
301+ } else {
302+ // Optimized path for standard types
303+ if !value_type. equals_datatype ( field_type) && !can_cast_types ( & value_type, field_type) {
304+ return plan_err ! ( "Cannot cast {} to {} for column {}" , value_type, field_type, column_name) ;
305+ }
291306 }
292307 }
293- let metadata = if field_metadata . is_empty ( ) {
294- None
295- } else {
296- Some ( field_metadata )
297- } ;
298- fields . push_with_metadata ( field_type . to_owned ( ) , field_nullable , metadata ) ;
308+
309+ fields . push_with_metadata (
310+ field_type . clone ( ) ,
311+ field . is_nullable ( ) ,
312+ Some ( FieldMetadata :: new_from_field ( field ) )
313+ ) ;
299314 }
300315
301316 Self :: infer_inner ( values, fields, schema)
0 commit comments