fix(transfer): preserve NUMBER(p,s) precision for oracle-family targets
This commit is contained in:
5 files changed
+64
-6
No files matched your search
@@ -3354,13 +3354,22 @@ pub fn map_column_type(source_type: &str, source_db: &DatabaseType, target_db: &
|
||||
},
|
||||
"decimal" | "numeric" | "number" => {
|
||||
if t.contains('(') {
|
||||
let params = &t[t.find('(').unwrap()..];
|
||||
match target_db {
|
||||
DatabaseType::Mysql | DatabaseType::SqlServer | DatabaseType::Oracle | DatabaseType::H2 => {
|
||||
format!("DECIMAL{}", &t[t.find('(').unwrap()..])
|
||||
}
|
||||
target_db if is_postgres_transfer_dialect(target_db) => {
|
||||
format!("DECIMAL{}", &t[t.find('(').unwrap()..])
|
||||
}
|
||||
// Oracle-family targets accept DECIMAL(p, s) as a synonym for
|
||||
// NUMBER(p, s). OceanBase's Oracle mode and Dameng used to fall into
|
||||
// the bare-NUMERIC fallback below, which is NUMBER(38, 0): transferring
|
||||
// Oracle NUMBER(14, 2) silently dropped every decimal (#9667).
|
||||
DatabaseType::Mysql
|
||||
| DatabaseType::SqlServer
|
||||
| DatabaseType::Oracle
|
||||
| DatabaseType::OceanbaseOracle
|
||||
| DatabaseType::Dameng
|
||||
| DatabaseType::H2 => format!("DECIMAL{params}"),
|
||||
target_db if is_postgres_transfer_dialect(target_db) => format!("DECIMAL{params}"),
|
||||
// Every other target keeps the historical bare spelling: its decimal
|
||||
// semantics are not the Oracle NUMBER synonym, so passing (p, s) through
|
||||
// needs per-engine verification and stays out of scope here.
|
||||
_ => "NUMERIC".into(),
|
||||
}
|
||||
} else {
|
||||
@@ -16333,6 +16342,55 @@ SELECT 1 FROM dual"#
|
||||
assert_eq!(map_column_type("NVARCHAR2(50 CHAR)", &DatabaseType::Oracle, &DatabaseType::Mysql), "TEXT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_column_type_keeps_number_precision_for_oracle_family_targets() {
|
||||
// Issue #9667: transferring an Oracle table to OceanBase(Oracle mode) generated a
|
||||
// bare `NUMERIC` for every NUMBER(p, s) column. In Oracle-compatible semantics that
|
||||
// is NUMBER(38, 0), so the target silently dropped every decimal (only the integer
|
||||
// part survived). Oracle-family targets must keep precision and scale.
|
||||
for target in [DatabaseType::OceanbaseOracle, DatabaseType::Dameng] {
|
||||
assert_eq!(map_column_type("NUMBER(14,2)", &DatabaseType::Oracle, &target), "DECIMAL(14,2)");
|
||||
assert_eq!(map_column_type("NUMBER(14,10)", &DatabaseType::Oracle, &target), "DECIMAL(14,10)");
|
||||
assert_eq!(map_column_type("NUMBER(15,0)", &DatabaseType::Oracle, &target), "DECIMAL(15,0)");
|
||||
assert_eq!(map_column_type("NUMBER(12,4)", &DatabaseType::Oracle, &target), "DECIMAL(12,4)");
|
||||
assert_eq!(map_column_type("NUMBER(10,-2)", &DatabaseType::Oracle, &target), "DECIMAL(10,-2)");
|
||||
// Spacing inside the parameter list is preserved verbatim, exactly like the
|
||||
// existing Mysql/H2/Oracle branches.
|
||||
assert_eq!(map_column_type("NUMBER(14, 2)", &DatabaseType::Oracle, &target), "DECIMAL(14, 2)");
|
||||
}
|
||||
|
||||
// A source type without parameters keeps the historical bare spelling: it carries
|
||||
// no precision to lose.
|
||||
assert_eq!(map_column_type("NUMBER", &DatabaseType::Oracle, &DatabaseType::OceanbaseOracle), "NUMERIC");
|
||||
assert_eq!(map_column_type("NUMBER", &DatabaseType::Oracle, &DatabaseType::Dameng), "NUMERIC");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_create_table_oracle_to_oceanbase_oracle_keeps_number_scale() {
|
||||
let columns = vec![
|
||||
db::ColumnInfo { is_primary_key: true, ..test_column("ID", "NUMBER(10)") },
|
||||
test_column("SUMZEROTAXPREMIUM", "NUMBER(14,2)"),
|
||||
test_column("XBCOMPANYRATE", "NUMBER(14,10)"),
|
||||
test_column("PLAINNUMBER", "NUMBER"),
|
||||
];
|
||||
|
||||
let ddl = generate_create_table_ddl(
|
||||
&columns,
|
||||
"TXF_PREC",
|
||||
"DBX_TEST",
|
||||
"DBX_TEST",
|
||||
&DatabaseType::OceanbaseOracle,
|
||||
&DatabaseType::Oracle,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(ddl.contains("\"SUMZEROTAXPREMIUM\" DECIMAL(14,2)"), "{ddl}");
|
||||
assert!(ddl.contains("\"XBCOMPANYRATE\" DECIMAL(14,10)"), "{ddl}");
|
||||
assert!(ddl.contains("\"ID\" DECIMAL(10)"), "{ddl}");
|
||||
assert!(ddl.contains("\"PLAINNUMBER\" NUMERIC"), "{ddl}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_column_type_oracle_plain_varchar2_to_mysql() {
|
||||
// Plain VARCHAR2 without a length unit must keep working unchanged.
|
||||
|
||||
Reference in new issue
Block a user