feat: add QuestDB support

Thanks @ptma!
This commit is contained in:
John Jin authored and GitHub committed 2026-06-14 22:58:05 +08:00
1 parent 0ae720d950
commit 1afc271c18
51 files changed
+633 -46

No files matched your search

@@ -551,6 +551,29 @@ pub(super) fn build_sqlite_existing_column_sql(
statements
}
pub(super) fn build_questdb_existing_column_sql(table: &str, column: &EditableStructureColumn) -> Vec<String> {
let Some(original) = &column.original else {
return Vec::new();
};
let mut statements = Vec::new();
let current_name = &column.name;
if column.name != original.name {
statements.push(format!(
"ALTER TABLE {table} RENAME COLUMN {} TO {};",
quote_ident(StructureDialect::Questdb, &original.name),
quote_ident(StructureDialect::Questdb, &column.name)
));
}
if column.data_type.trim() != original.data_type.trim() {
statements.push(format!(
"ALTER TABLE {table} ALTER COLUMN {} TYPE {};",
quote_ident(StructureDialect::Questdb, current_name),
column_data_type(StructureDialect::Questdb, column)
));
}
statements
}
pub(super) fn has_existing_column_attribute_change(column: &EditableStructureColumn) -> bool {
let Some(original) = &column.original else {
return false;
@@ -5,6 +5,11 @@ use super::util::{clean, format_default_for_sql, normalize_default, quote_ident,
pub(super) fn column_definition(dialect: StructureDialect, column: &EditableStructureColumn) -> String {
let data_type = column_data_type(dialect, column);
let mut parts = vec![quote_ident(dialect, &column.name), data_type];
// QuestDB SQL Syntax: ```ALTER TABLE tableName ADD COLUMN [IF NOT EXISTS] columnName typeDef```
if dialect == StructureDialect::Questdb {
parts.insert(0, "IF NOT EXISTS".to_string());
return parts.join(" ");
}
if !column.is_nullable && !is_oracle_like(dialect) && dialect != StructureDialect::ClickHouse {
parts.push("NOT NULL".to_string());
}
@@ -74,6 +79,9 @@ pub(super) fn column_data_type(dialect: StructureDialect, column: &EditableStruc
if dialect == StructureDialect::ManticoreSearch {
return manticore_column_type(column);
}
if dialect == StructureDialect::Questdb {
return questdb_column_type(column);
}
normalize_column_data_type(dialect, &column.data_type)
}
@@ -186,3 +194,22 @@ pub(super) fn unwrap_clickhouse_nullable_type(data_type: &str) -> String {
trimmed.to_string()
}
}
/// QuestDB 类型处理
/// geohash, decimal 这2种类型才能带()
pub(super) fn questdb_column_type(column: &EditableStructureColumn) -> String {
let data_type = column.data_type.trim().to_ascii_lowercase();
let length_types: [&str; 2] = ["geohash", "decimal"];
match data_type.find('(') {
Some(pos) => {
let base_type = &data_type[..pos];
let params = &data_type[pos..];
if length_types.contains(&base_type) {
format!("{}{}", base_type, params)
} else {
base_type.to_string()
}
}
None => data_type,
}
}
@@ -1,8 +1,8 @@
use super::column_alter::{
build_clickhouse_existing_column_sql, build_h2_existing_column_sql, build_informix_existing_column_sql,
build_mysql_existing_column_sql, build_oracle_like_existing_column_sql, build_postgres_existing_column_sql,
build_sqlite_existing_column_sql, build_sqlserver_existing_column_sql, has_column_extra_change,
has_existing_column_attribute_change,
build_questdb_existing_column_sql, build_sqlite_existing_column_sql, build_sqlserver_existing_column_sql,
has_column_extra_change, has_existing_column_attribute_change,
};
use super::column_format::column_definition;
use super::comments::build_sqlserver_column_comment_sql;
@@ -120,6 +120,7 @@ pub(super) fn build_column_sql(options: &TableStructureSqlOptions, warnings: &mu
&options.table_name,
)),
StructureDialect::Sqlite => statements.extend(build_sqlite_existing_column_sql(&table, column, warnings)),
StructureDialect::Questdb => statements.extend(build_questdb_existing_column_sql(&table, column)),
_ => warnings.push(format!("Editing existing columns is not supported for {database_label} yet.")),
}
}
@@ -13,6 +13,7 @@ pub(super) enum StructureDialect {
ClickHouse,
ManticoreSearch,
Informix,
Questdb,
Unsupported,
}
@@ -116,6 +117,23 @@ pub(super) fn capabilities_for(database_type: Option<DatabaseType>) -> TableStru
alter_primary_key: true,
..base
},
Some(DatabaseType::Questdb) => TableStructureCapabilities {
dialect: StructureDialect::Questdb,
add_column: true,
drop_column: true,
rename_column: true,
alter_existing_column: true,
reorder_column: false,
comment: false,
create_index: false,
drop_index: false,
rebuild_index: false,
index_type: false,
index_include: false,
index_filter: false,
index_comment: false,
alter_primary_key: false,
},
Some(DatabaseType::Redshift | DatabaseType::Vertica) => TableStructureCapabilities {
dialect: StructureDialect::Postgres,
add_column: true,
@@ -254,6 +272,7 @@ pub(super) fn dialect_label(dialect: StructureDialect) -> String {
StructureDialect::ClickHouse => "clickhouse",
StructureDialect::ManticoreSearch => "manticoresearch",
StructureDialect::Informix => "informix",
StructureDialect::Questdb => "questdb",
StructureDialect::Unsupported => "this database",
}
.to_string()
@@ -272,6 +291,7 @@ pub(super) fn database_type_for_dialect(dialect: StructureDialect) -> Option<Dat
StructureDialect::ClickHouse => Some(DatabaseType::ClickHouse),
StructureDialect::ManticoreSearch => Some(DatabaseType::ManticoreSearch),
StructureDialect::Informix => Some(DatabaseType::Informix),
StructureDialect::Questdb => Some(DatabaseType::Questdb),
StructureDialect::Unsupported => None,
}
}
@@ -18,7 +18,9 @@ pub(super) fn qualified_table(dialect: StructureDialect, schema: Option<&str>, t
pub(super) fn quote_ident(dialect: StructureDialect, name: &str) -> String {
match dialect {
StructureDialect::Mysql | StructureDialect::ManticoreSearch => format!("`{}`", name.replace('`', "``")),
StructureDialect::Mysql | StructureDialect::ManticoreSearch | StructureDialect::Questdb => {
format!("`{}`", name.replace('`', "``"))
}
StructureDialect::SqlServer => format!("[{}]", name.replace(']', "]]")),
StructureDialect::Informix if is_simple_informix_identifier(name) => name.to_string(),
_ => format!("\"{}\"", name.replace('"', "\"\"")),