(
scx: &StatementContext,
table: &PostgresTableDesc,
text_columns: &Vec<Ident>,
)
| 551 | } |
| 552 | |
| 553 | pub(crate) fn generate_column_casts( |
| 554 | scx: &StatementContext, |
| 555 | table: &PostgresTableDesc, |
| 556 | text_columns: &Vec<Ident>, |
| 557 | ) -> Result<Vec<(CastType, StorageScalarExpr)>, PlanError> { |
| 558 | // Generate the cast expressions required to convert the text encoded columns into |
| 559 | // the appropriate target types, creating a Vec<StorageScalarExpr>. |
| 560 | // The postgres source reader will then eval each of those on the incoming rows. |
| 561 | |
| 562 | let text_columns = BTreeSet::from_iter(text_columns.iter().map(Ident::as_str)); |
| 563 | |
| 564 | let mut table_cast = vec![]; |
| 565 | for (i, column) in table.columns.iter().enumerate() { |
| 566 | let (cast_type, ty) = if text_columns.contains(column.name.as_str()) { |
| 567 | // Treat the column as text if it was referenced in |
| 568 | // `TEXT COLUMNS`. This is the only place we need to |
| 569 | // perform this logic; even if the type is unsupported, |
| 570 | // we'll be able to ingest its values as text in |
| 571 | // storage. |
| 572 | (CastType::Text, mz_pgrepr::Type::Text) |
| 573 | } else { |
| 574 | match mz_pgrepr::Type::from_oid_and_typmod(column.type_oid, column.type_mod) { |
| 575 | Ok(t) => (CastType::Natural, t), |
| 576 | // If this reference survived purification, we |
| 577 | // do not expect it to be from a table that the |
| 578 | // user will consume., i.e. expect this table to |
| 579 | // be filtered out of table casts. |
| 580 | Err(_) => { |
| 581 | table_cast.push(( |
| 582 | CastType::Natural, |
| 583 | StorageScalarExpr::ErrorIfNull( |
| 584 | Box::new(StorageScalarExpr::Literal( |
| 585 | Row::pack_slice(&[Datum::Null]), |
| 586 | ReprColumnType { |
| 587 | nullable: true, |
| 588 | scalar_type: ReprScalarType::String, |
| 589 | }, |
| 590 | )), |
| 591 | format!("Unsupported type with OID {}", column.type_oid), |
| 592 | ), |
| 593 | )); |
| 594 | continue; |
| 595 | } |
| 596 | } |
| 597 | }; |
| 598 | |
| 599 | let cast_expr = match pg_type_to_cast_func(scx, &ty) { |
| 600 | Ok(None) => { |
| 601 | // No cast needed (e.g. Text → String identity). |
| 602 | StorageScalarExpr::Column(i) |
| 603 | } |
| 604 | Ok(Some(cast_func)) => { |
| 605 | StorageScalarExpr::CallUnary(cast_func, Box::new(StorageScalarExpr::Column(i))) |
| 606 | } |
| 607 | Err(PlanError::TableContainsUningestableTypes { type_, .. }) => { |
| 608 | // We expect only reg* types and similar to encounter |
| 609 | // this. Users can ingest the data as text if they need |
| 610 | // to. This is acceptable because we don't expect the |
no test coverage detected