{"record":{"id":"8dd81be15ee83317","repo":"dbt-labs/dbt-core","slug":"column-name-normalization-preserves-schema-compati","errorCode":null,"errorMessage":"column name normalization preserves schema compatibility","messagePattern":"column name normalization preserves schema compatibility","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/metadata/snowflake/mod.rs","lineNumber":406,"sourceCode":"\npub const ARROW_FIELD_SNOWFLAKE_FIELD_WIDTH_METADATA_KEY: &str = \"SNOWFLAKE:field_width\";\n\n/// Normalize all column names in a RecordBatch to lowercase.\n///\n/// Snowflake may uppercase column aliases (e.g. `table_catalog as \"table_database\"`) depending\n/// on account-level settings, even when the alias is double-quoted. Lowercasing the schema up\n/// front lets all downstream `get_column_values` calls use their expected lowercase names without\n/// needing per-call case-insensitive logic.\nfn lowercase_column_names(batch: &RecordBatch) -> RecordBatch {\n    let schema = batch.schema();\n    let fields: Vec<_> = schema\n        .fields()\n        .iter()\n        .map(|f| Arc::new(f.as_ref().clone().with_name(f.name().to_lowercase())))\n        .collect();\n    let new_schema = Arc::new(Schema::new_with_metadata(fields, schema.metadata().clone()));\n    RecordBatch::try_new(new_schema, batch.columns().to_vec())\n        .expect(\"column name normalization preserves schema compatibility\")\n}\n\nfn accumulate_view_definition_fetch_result(\n    acc: &mut ViewDefinitionFetchResult,\n    batch: &RecordBatch,\n) -> AdapterResult<()> {\n    let batch = lowercase_column_names(batch);\n    // Result schema: (fqn STRING, view_definition STRING, error STRING)\n    let fqns_arr = batch.column_values::<StringArray>(\"fqn\")?;\n    let defs_arr = batch.column_values::<StringArray>(\"view_definition\")?;\n\n    for i in 0..batch.num_rows() {\n        let fqn = fqns_arr.value(i).to_string();\n        if defs_arr.is_null(i) {\n            // A NULL definition means Snowflake could not return DDL for\n            // this relation. Treat it as unresolvable regardless of the\n            // exact GET_DDL error text so the query cache can fall back\n            // to freshness metadata for secure/data-share views.","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/metadata/snowflake/mod.rs#L388-L424","documentation":"`lowercase_column_names` (crates/dbt-adapter/src/metadata/snowflake/mod.rs:406) rebuilds a `RecordBatch` with lowercased field names via `RecordBatch::try_new(...).expect(...)`. Arrow guarantees `try_new` succeeds when the new schema has the same field count, types, and nullability as the original columns; the panic fires only if a column's type/nullability no longer matches the schema.","triggerScenarios":"Lowercasing columns of a batch whose fields have mismatched Arrow data types or nullability flags relative to the actual column arrays (e.g. a producer built the batch with inconsistent schema/columns).","commonSituations":"Upstream Arrow version changes altering nullability defaults; adapter code building batches with schema/column type drift; dictionary or view-typed columns whose rebuilt schema loses an attribute.","solutions":["Verify the source batch was constructed with a schema matching its columns; fix the producer","Propagate the `try_new` error instead of expecting, so the offending schema is reported","Ensure `with_name` preserves all field attributes (nullability, metadata, extensions) when cloning fields","Pin compatible arrow-rs versions across the workspace"],"exampleFix":"// before\n.expect(\"column name normalization preserves schema compatibility\")\n// after\n.map_err(|e| AdapterError::new(AdapterErrorKind::UnexpectedResult, e.to_string()))?","handlingStrategy":"validation","validationCode":"// verify batch schema matches columns before lowering names\nassert_eq!(schema.fields().len(), batch.num_columns());\nassert!(schema.fields().iter().zip(batch.columns()).all(|(f, c)| f.data_type() == c.data_type()));","typeGuard":"fn batch_matches_schema(batch: &RecordBatch) -> bool {\n    batch.schema().fields().len() == batch.num_columns()\n        && batch.schema().fields().iter().zip(batch.columns()).all(|(f, c)| f.data_type() == c.data_type())\n}","tryCatchPattern":"match RecordBatch::try_new(new_schema, cols) { Ok(b) => b, Err(e) => return Err(...) }","preventionTips":["Build RecordBatches only through try_new so mismatches surface early","Use with_name on cloned fields preserving nullability/metadata","Keep arrow-rs versions aligned workspace-wide","Test lowercase_column_names against all Snowflake column type variants"],"tags":["rust","arrow","schema","snowflake"],"backgroundTag":"schema-validation-failed","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}