fix(gbase): generate GBase 8s table DDL with Informix dialect

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-09-04 16:48:35 +08:00
1 parent d430ec9a4a
commit af32936297
20 files changed
+284 -16

No files matched your search

@@ -14,7 +14,7 @@ use super::util::{
use crate::table_structure_sql::ColumnExtra;
pub fn build_single_column_alter_sql(options: SingleColumnAlterSqlOptions) -> TableStructureSqlResult {
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
let dialect = capabilities.dialect;
let table = qualified_table(dialect, options.schema.as_deref(), &options.table_name);
let database_label = database_label(options.database_type);
@@ -22,7 +22,7 @@ use crate::models::connection::DatabaseType;
use std::collections::HashSet;
pub(super) fn build_column_sql(options: &TableStructureSqlOptions, warnings: &mut Vec<String>) -> Vec<String> {
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
let dialect = capabilities.dialect;
let table = qualified_table(dialect, options.schema.as_deref(), &options.table_name);
let database_label = database_label(options.database_type);
@@ -383,7 +383,7 @@ pub(super) fn validate_primary_key_change_scope(options: &TableStructureSqlOptio
}
let Some(change) = primary_key_change(options) else { return Vec::new() };
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
let supported =
if change.old_ids.is_empty() { capabilities.add_primary_key } else { capabilities.alter_primary_key };
if supported {
@@ -452,7 +452,7 @@ pub(super) fn build_primary_key_sql(
table: &str,
warnings: &mut Vec<String>,
) -> Vec<String> {
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
// Membership by draft id (set equality): pure rename / local reorder of the same key
// columns is not a PK change. A draft that drops a PK column is also rejected here.
@@ -3,7 +3,7 @@ use super::types::TableStructureSqlOptions;
use super::util::{clean, qualified_table, quote_string};
pub(super) fn build_table_comment_sql(options: &TableStructureSqlOptions, warnings: &mut Vec<String>) -> Vec<String> {
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
let new_comment = options.table_comment.as_deref().unwrap_or("");
let original_comment = options.original_table_comment.as_deref().unwrap_or("");
if clean(new_comment) == clean(original_comment) {
@@ -22,7 +22,7 @@ pub fn build_create_table_sql(mut options: TableStructureSqlOptions) -> TableStr
capabilities = super::dialect::gaussdb_m_capabilities();
dialect = StructureDialect::GaussdbM;
} else {
capabilities = capabilities_for(options.database_type);
capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
dialect = capabilities.dialect;
}
options.table_name = clean(&options.table_name);
@@ -93,7 +93,10 @@ pub(super) fn gaussdb_m_capabilities() -> TableStructureCapabilities {
}
}
pub(super) fn capabilities_for(database_type: Option<DatabaseType>) -> TableStructureCapabilities {
pub(super) fn capabilities_for(
database_type: Option<DatabaseType>,
driver_profile: Option<&str>,
) -> TableStructureCapabilities {
let base = TableStructureCapabilities::default();
match database_type {
Some(
@@ -129,6 +132,23 @@ pub(super) fn capabilities_for(database_type: Option<DatabaseType>) -> TableStru
comment: true,
..base
},
// GBase 8s is Informix-compatible, unlike the rest of the `Gbase`
// family (GBase 8a and friends) which speak MySQL wire protocol and
// SQL dialect. `driver_profile` is the only signal that distinguishes
// them (see the same check in `table_export.rs` / `table_select.rs`).
Some(DatabaseType::Gbase) if driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s")) => {
TableStructureCapabilities {
dialect: StructureDialect::Informix,
add_column: true,
drop_column: true,
rename_column: true,
alter_existing_column: true,
create_index: true,
drop_index: true,
rebuild_index: true,
..base
}
}
Some(DatabaseType::Gbase) => TableStructureCapabilities {
dialect: StructureDialect::Mysql,
add_column: true,
@@ -23,7 +23,7 @@ fn build_foreign_key_sql_with_mode(
}
let database_label = database_label(options.database_type);
let capabilities = super::dialect::capabilities_for(options.database_type);
let capabilities = super::dialect::capabilities_for(options.database_type, options.driver_profile.as_deref());
let dialect = capabilities.dialect;
if !capabilities.foreign_key {
if has_foreign_key_edits(&options.foreign_keys) {
@@ -12,7 +12,7 @@ pub(super) fn build_index_sql(options: &TableStructureSqlOptions, warnings: &mut
capabilities = caps;
dialect = StructureDialect::GaussdbM;
} else {
let caps = capabilities_for(options.database_type);
let caps = capabilities_for(options.database_type, options.driver_profile.as_deref());
capabilities = caps;
dialect = caps.dialect;
}
@@ -346,7 +346,7 @@ pub(super) fn build_create_index_statements(
concurrently_supported: bool,
for_new_table: bool,
) -> Vec<String> {
let capabilities = capabilities_for(database_type_for_dialect(dialect));
let capabilities = capabilities_for(database_type_for_dialect(dialect), None);
let name = clean(&index.name);
let columns: Vec<String> =
index.columns.iter().map(|column| clean(column)).filter(|column| !column.is_empty()).collect();
@@ -1484,6 +1484,7 @@ mod tests {
) -> TableStructureSqlOptions {
TableStructureSqlOptions {
database_type: Some(DatabaseType::Sqlite),
driver_profile: None,
schema: None,
table_name: table_name.to_string(),
columns: vec![EditableStructureColumn {
File diff suppressed because it is too large. Load diff
@@ -22,7 +22,7 @@ fn build_trigger_sql_with_mode(
return Vec::new();
}
let dialect = super::dialect::capabilities_for(options.database_type).dialect;
let dialect = super::dialect::capabilities_for(options.database_type, options.driver_profile.as_deref()).dialect;
let database_label = database_label(options.database_type);
if !matches!(dialect, StructureDialect::Mysql | StructureDialect::Oracle | StructureDialect::SqlServer) {
if options.triggers.iter().any(has_trigger_edit) {
@@ -207,6 +207,11 @@ pub struct TriggerInfo {
pub struct TableStructureSqlOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub database_type: Option<DatabaseType>,
/// Driver profile reported by the connection (e.g. `"gbase8s"`). GBase 8s
/// is Informix-compatible rather than MySQL-compatible like the rest of
/// the `Gbase` family, so this disambiguates which dialect to generate.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver_profile: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
pub table_name: String,
@@ -281,6 +286,8 @@ pub struct SingleColumnAlterSqlOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub database_type: Option<DatabaseType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver_profile: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
pub table_name: String,
pub column: EditableStructureColumn,
@@ -15,7 +15,7 @@ use super::util::clean;
/// a concurrent request in the first place).
pub(super) fn validate_concurrent_index_scope(options: &TableStructureSqlOptions) -> Vec<String> {
let mut errors = Vec::new();
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
if !capabilities.index_concurrent || capabilities.dialect != StructureDialect::Postgres {
return errors;
}
@@ -67,7 +67,7 @@ pub(super) fn validate_dameng_identity(
columns: &[&EditableStructureColumn],
warnings: &mut Vec<String>,
) {
if capabilities_for(options.database_type).dialect != StructureDialect::Dameng {
if capabilities_for(options.database_type, options.driver_profile.as_deref()).dialect != StructureDialect::Dameng {
return;
}
@@ -109,7 +109,7 @@ pub(super) fn validate_columns(columns: &[&EditableStructureColumn], warnings: &
/// classes are almost always needed. This is a warning, not an error, because
/// the default class may be deliberate in some cases.
fn validate_gin_opclass_warnings(options: &TableStructureSqlOptions, warnings: &mut Vec<String>) {
let capabilities = capabilities_for(options.database_type);
let capabilities = capabilities_for(options.database_type, options.driver_profile.as_deref());
if capabilities.dialect != StructureDialect::Postgres {
return;
}