Plan `UPDATE target SET ... FROM src WHERE target.col = src.col ...`.
(update: &ast::Update, catalog: &dyn SqlCatalog)
| 58 | |
| 59 | /// Plan `UPDATE target SET ... FROM src WHERE target.col = src.col ...`. |
| 60 | fn plan_update_from(update: &ast::Update, catalog: &dyn SqlCatalog) -> Result<Vec<SqlPlan>> { |
| 61 | let target_name = extract_table_name_from_table_with_joins(&update.table)?; |
| 62 | |
| 63 | // Extract alias for the target table if present. |
| 64 | let target_alias: Option<String> = match &update.table.relation { |
| 65 | ast::TableFactor::Table { alias, .. } => alias.as_ref().map(|a| normalize_ident(&a.name)), |
| 66 | _ => None, |
| 67 | }; |
| 68 | let target_ref = target_alias.as_deref().unwrap_or(target_name.as_str()); |
| 69 | |
| 70 | let from_kind = update.from.as_ref().expect("caller ensures from.is_some()"); |
| 71 | let from_tables: &Vec<ast::TableWithJoins> = match from_kind { |
| 72 | ast::UpdateTableFromKind::AfterSet(tables) |
| 73 | | ast::UpdateTableFromKind::BeforeSet(tables) => tables, |
| 74 | }; |
| 75 | |
| 76 | // Reject multi-table FROM. |
| 77 | if from_tables.len() > 1 { |
| 78 | return Err(SqlError::Unsupported { |
| 79 | detail: format!( |
| 80 | "UPDATE ... FROM with {} source tables is not supported; \ |
| 81 | only a single FROM table is accepted", |
| 82 | from_tables.len() |
| 83 | ), |
| 84 | }); |
| 85 | } |
| 86 | let from_table = from_tables.first().ok_or_else(|| SqlError::Parse { |
| 87 | detail: "UPDATE ... FROM requires at least one source table".into(), |
| 88 | })?; |
| 89 | |
| 90 | // Reject subquery in FROM. |
| 91 | let source_name = match &from_table.relation { |
| 92 | ast::TableFactor::Table { name, .. } => normalize_object_name_checked(name)?, |
| 93 | ast::TableFactor::Derived { .. } => { |
| 94 | return Err(SqlError::Unsupported { |
| 95 | detail: "UPDATE ... FROM (subquery) is not supported; \ |
| 96 | use a CTE: WITH cte AS (SELECT ...) UPDATE t SET ... FROM cte WHERE ..." |
| 97 | .into(), |
| 98 | }); |
| 99 | } |
| 100 | _ => { |
| 101 | return Err(SqlError::Unsupported { |
| 102 | detail: "non-table relation in UPDATE ... FROM is not supported".into(), |
| 103 | }); |
| 104 | } |
| 105 | }; |
| 106 | // Reject joins in the FROM source. |
| 107 | if !from_table.joins.is_empty() { |
| 108 | return Err(SqlError::Unsupported { |
| 109 | detail: "JOIN in UPDATE ... FROM source is not supported; \ |
| 110 | use a CTE to pre-join the source" |
| 111 | .into(), |
| 112 | }); |
| 113 | } |
| 114 | |
| 115 | let source_alias: Option<String> = match &from_table.relation { |
| 116 | ast::TableFactor::Table { alias, .. } => alias.as_ref().map(|a| normalize_ident(&a.name)), |
| 117 | _ => None, |
no test coverage detected