mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(transfer): reseed Oracle identity columns after copying explicit ids
This commit is contained in:
@@ -2858,6 +2858,50 @@ fn generate_postgres_sequence_sync_sql(columns: &[db::ColumnInfo], table: &str,
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// `ALTER TABLE ... MODIFY <column> GENERATED BY DEFAULT AS IDENTITY (START WITH LIMIT VALUE)`
|
||||
/// statements for Oracle targets.
|
||||
///
|
||||
/// Oracle accepts explicit values in a `GENERATED BY DEFAULT AS IDENTITY` column but
|
||||
/// does not advance its sequence, so the first auto-generated insert after copying
|
||||
/// explicit ids can collide with a copied value. `START WITH LIMIT VALUE` makes Oracle
|
||||
/// lock the table and move the sequence past the maximum value in the column.
|
||||
///
|
||||
/// The filter mirrors the identity emission in
|
||||
/// `generate_create_table_ddl_with_column_quoting` (primary key, explicit
|
||||
/// auto-increment, integer-like mapped type), so the ALTER only targets a column the
|
||||
/// generated DDL created as an identity column.
|
||||
fn oracle_identity_reseed_statements(
|
||||
columns: &[db::ColumnInfo],
|
||||
table: &str,
|
||||
schema: &str,
|
||||
source_schema: &str,
|
||||
source_db: &DatabaseType,
|
||||
quote_target_column_names: bool,
|
||||
) -> Vec<String> {
|
||||
let full_table = qualified_table(table, schema, &DatabaseType::Oracle, None);
|
||||
columns
|
||||
.iter()
|
||||
.filter(|column| {
|
||||
column.is_primary_key
|
||||
&& column_is_auto_increment(column)
|
||||
&& type_looks_integer(&postgres_column_type_sql(
|
||||
column,
|
||||
source_schema,
|
||||
schema,
|
||||
source_db,
|
||||
&DatabaseType::Oracle,
|
||||
))
|
||||
})
|
||||
.map(|column| {
|
||||
let quoted_column =
|
||||
transfer_column_identifier(&column.name, &DatabaseType::Oracle, quote_target_column_names);
|
||||
format!(
|
||||
"ALTER TABLE {full_table} MODIFY {quoted_column} GENERATED BY DEFAULT AS IDENTITY (START WITH LIMIT VALUE)"
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Explicit identity values do not advance Xugu's backing sequence. Resolve the
|
||||
/// sequence through SERIAL_ID rather than relying on its generated name, which
|
||||
/// belongs to SYSDBA even when the table lives in another schema.
|
||||
@@ -12257,6 +12301,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Oracle accepts explicit ids in a `GENERATED BY DEFAULT AS IDENTITY` column but
|
||||
// does not advance the sequence, so move it past the copied values. Only for
|
||||
// tables this run created — `START WITH LIMIT VALUE` requires the identity column
|
||||
// the generated DDL just created, and a preexisting target may not have one.
|
||||
// OceanBase Oracle and DM8 stay out until their behavior is verified.
|
||||
if *target_db_type == DatabaseType::Oracle
|
||||
&& request.create_table
|
||||
&& !target_table_preexisting
|
||||
&& total_transferred > 0
|
||||
{
|
||||
for statement in oracle_identity_reseed_statements(
|
||||
&columns,
|
||||
&target_table,
|
||||
&request.target_schema,
|
||||
&request.source_schema,
|
||||
source_db_type,
|
||||
request.quote_target_column_names,
|
||||
) {
|
||||
execute_on_pool(state, target_pool_key, &statement)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to reseed the Oracle identity column for {target_table}: {e}"))?;
|
||||
}
|
||||
}
|
||||
|
||||
if should_restore_postgres_table_schema {
|
||||
restore_postgres_table_schema_objects(
|
||||
state,
|
||||
@@ -19024,6 +19092,31 @@ PARTITION p_old VALUES LESS THAN (TO_DAYS('2026-01-01')))";
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oracle_identity_reseed_targets_generated_identity_columns() {
|
||||
let columns = vec![
|
||||
db::ColumnInfo {
|
||||
is_primary_key: true,
|
||||
extra: Some("auto_increment".to_string()),
|
||||
..test_column("id", "int")
|
||||
},
|
||||
// Not a key on the source: the generated DDL keeps it a plain column.
|
||||
db::ColumnInfo { extra: Some("auto_increment".to_string()), ..test_column("ticket", "int") },
|
||||
// Plain integer primary key without an explicit auto-increment stays plain.
|
||||
db::ColumnInfo { is_primary_key: true, ..test_column("tenant", "int") },
|
||||
];
|
||||
|
||||
let statements =
|
||||
oracle_identity_reseed_statements(&columns, "users", "SALES", "source", &DatabaseType::Mysql, true);
|
||||
|
||||
assert_eq!(
|
||||
statements,
|
||||
vec![
|
||||
"ALTER TABLE \"SALES\".\"users\" MODIFY \"id\" GENERATED BY DEFAULT AS IDENTITY (START WITH LIMIT VALUE)".to_string()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn postgres_sequence_sync_sql_casts_text_and_numeric_columns_to_bigint() {
|
||||
let sql = generate_postgres_sequence_sync_sql(
|
||||
|
||||
Reference in New Issue
Block a user