fix: combine Oracle-like MODIFY statements and fix DATA_DEFAULT column ordering

- table_structure_sql: merge separate MODIFY statements for type, nullability,
  and default into a single MODIFY statement. This fixes Dameng's requirement
  that MODIFY includes the full column definition.
- DbxJdbcPlugin: move DATA_DEFAULT to first SELECT position and read it first
  in the ResultSet loop (LONG columns must be read before other columns).
This commit is contained in:
t8y2 2026-06-04 16:57:44 +08:00
parent 01c8072d97
commit 834dc534c6
2 changed files with 25 additions and 19 deletions

View File

@ -960,24 +960,26 @@ fn build_oracle_like_existing_column_sql(
));
current_name = column.name.clone();
}
if column.data_type.trim() != original.data_type.trim() {
statements.push(format!(
"ALTER TABLE {table} MODIFY ({} {});",
quote_ident(dialect, &current_name),
column_data_type(dialect, column)
));
}
if column.is_nullable != original.is_nullable {
let nullability = if column.is_nullable { "NULL" } else { "NOT NULL" };
statements.push(format!("ALTER TABLE {table} MODIFY ({} {nullability});", quote_ident(dialect, &current_name)));
}
if normalize_default(Some(&column.default_value)) != original_default(column) {
let type_changed = column.data_type.trim() != original.data_type.trim();
let nullable_changed = column.is_nullable != original.is_nullable;
let default_changed = normalize_default(Some(&column.default_value)) != original_default(column);
if type_changed || nullable_changed || default_changed {
let data_type = column_data_type(dialect, column);
let mut parts = vec![quote_ident(dialect, &current_name), data_type];
// Always include nullability so the statement is self-contained (required by Dameng).
if !column.is_nullable {
parts.push("NOT NULL".to_string());
} else {
parts.push("NULL".to_string());
}
let default_value = normalize_default(Some(&column.default_value));
let default_value = if default_value.is_empty() { "NULL".to_string() } else { default_value };
statements.push(format!(
"ALTER TABLE {table} MODIFY ({} DEFAULT {default_value});",
quote_ident(dialect, &current_name)
));
if !default_value.is_empty() {
parts.push(format!("DEFAULT {default_value}"));
} else if default_changed {
// User cleared the default — explicitly drop it.
parts.push("DEFAULT NULL".to_string());
}
statements.push(format!("ALTER TABLE {table} MODIFY ({});", parts.join(" ")));
}
if clean(&column.comment) != original_comment(column) {
let comment_value =

View File

@ -1018,8 +1018,10 @@ public final class DbxJdbcPlugin {
ArrayNode result = MAPPER.createArrayNode();
String resolvedTable = oracleResolveTable(conn, owner, table);
Set<String> pks = oraclePrimaryKeys(conn, owner, resolvedTable);
// data_default is a LONG column it must be read first in JDBC, before any other
// column, otherwise the data is truncated. We put it at position 1 for this reason.
String sql =
"SELECT c.column_name, c.data_type, c.nullable, c.data_default, " +
"SELECT c.data_default, c.column_name, c.data_type, c.nullable, " +
"c.data_precision, c.data_scale, c.char_length, cc.comments " +
"FROM all_tab_columns c " +
"LEFT JOIN all_col_comments cc ON cc.owner = c.owner AND cc.table_name = c.table_name AND cc.column_name = c.column_name " +
@ -1029,11 +1031,13 @@ public final class DbxJdbcPlugin {
ps.setString(2, resolvedTable);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
// data_default is a LONG read it first, before all other columns.
String dataDefault = rs.getString("data_default");
String name = rs.getString("column_name");
ObjectNode item = columnNode(result, name);
item.put("data_type", rs.getString("data_type"));
item.put("is_nullable", !"N".equals(rs.getString("nullable")));
putNullablePreferValue(item, "column_default", rs.getString("data_default"));
putNullablePreferValue(item, "column_default", dataDefault);
item.put("is_primary_key", pks.contains(name));
item.putNull("extra");
putNullablePreferValue(item, "comment", rs.getString("comments"));