diff --git a/crates/dbx-core/src/table_structure_sql.rs b/crates/dbx-core/src/table_structure_sql.rs index 66b02783c..16ce82bfe 100644 --- a/crates/dbx-core/src/table_structure_sql.rs +++ b/crates/dbx-core/src/table_structure_sql.rs @@ -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, ¤t_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, ¤t_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, ¤t_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, ¤t_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 = diff --git a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java index bf814f623..13f12ceee 100644 --- a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java +++ b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java @@ -1018,8 +1018,10 @@ public final class DbxJdbcPlugin { ArrayNode result = MAPPER.createArrayNode(); String resolvedTable = oracleResolveTable(conn, owner, table); Set 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"));