diff --git a/datafusion/sql/src/values.rs b/datafusion/sql/src/values.rs index 1716dd2cc7fe..6a7d8e453a14 100644 --- a/datafusion/sql/src/values.rs +++ b/datafusion/sql/src/values.rs @@ -420,11 +420,12 @@ impl SqlToRel<'_, S> { }) .collect::>(); if array_columns.is_empty() { - return if schema.fields().is_empty() { + let plan = if schema.fields().is_empty() { LogicalPlanBuilder::values(values)?.build() } else { LogicalPlanBuilder::values_with_schema(values, &schema)?.build() }; + return self.normalize_values_column_names(plan?); } for row in &values { for expr in row { @@ -560,10 +561,48 @@ impl SqlToRel<'_, S> { Default::default(), )?) }; - if values_schema.fields().is_empty() { + let plan = if values_schema.fields().is_empty() { LogicalPlanBuilder::values(values)?.build() } else { LogicalPlanBuilder::values_with_schema(values, &values_schema)?.build() + }; + self.normalize_values_column_names(plan?) + } + + fn normalize_values_column_names( + &self, + mut plan: LogicalPlan, + ) -> Result { + if !self.options.enable_ident_normalization + || !self + .context_provider + .options() + .sql_parser + .uppercase_unquoted_identifiers + { + return Ok(plan); + } + + if let LogicalPlan::Values(values) = &mut plan { + let fields = values + .schema + .fields() + .iter() + .map(|field| { + Arc::new( + field + .as_ref() + .clone() + .with_name(field.name().to_ascii_uppercase()), + ) + }) + .collect::>(); + values.schema = Arc::new(DFSchema::from_unqualified_fields( + fields.into(), + values.schema.metadata().clone(), + )?); } + + Ok(plan) } } diff --git a/datafusion/sql/tests/sql_integration.rs b/datafusion/sql/tests/sql_integration.rs index dbb6a50d1ab1..27a69da7b1fe 100644 --- a/datafusion/sql/tests/sql_integration.rs +++ b/datafusion/sql/tests/sql_integration.rs @@ -3696,6 +3696,36 @@ fn uppercase_unquoted_identifiers_apply_to_schema_and_table_ddl() { ); } +#[test] +fn uppercase_unquoted_identifiers_apply_to_values_columns() { + let default = logical_plan("VALUES (1, 'x')").unwrap(); + assert_eq!(default.schema().field(0).name(), "column1"); + assert_eq!(default.schema().field(1).name(), "column2"); + + let uppercase = + logical_plan_with_uppercase_unquoted_identifiers("VALUES (1, 'x')").unwrap(); + assert_eq!(uppercase.schema().field(0).name(), "COLUMN1"); + assert_eq!(uppercase.schema().field(1).name(), "COLUMN2"); + logical_plan_with_uppercase_unquoted_identifiers("SELECT column1 FROM (VALUES (1))") + .unwrap(); + assert!( + logical_plan_with_uppercase_unquoted_identifiers( + "SELECT \"column1\" FROM (VALUES (1))", + ) + .is_err() + ); + + let mut config = datafusion_common::config::ConfigOptions::default(); + config.sql_parser.uppercase_unquoted_identifiers = true; + let unnormalized = logical_plan_with_config_and_options( + "VALUES (1)", + config, + ParserOptions::default().with_enable_ident_normalization(false), + ) + .unwrap(); + assert_eq!(unnormalized.schema().field(0).name(), "column1"); +} + #[test] fn uppercase_unquoted_prepared_statement_names() { use datafusion_expr::logical_plan::Statement as PlanStatement;