feat(editor): add table copy/paste keyboard shortcuts
* feat: add table copy/paste with configurable structure and data options - Implement Navicat-style table copy/paste functionality - Support multi-table selection and batch operations - Configurable paste modes: structure+data (default), structure only, data only - Backend: add build_copy_table_data_sql API (INSERT INTO ... SELECT) - TreeItem.vue: copy/paste context menus, paste dialog with mode selection - ObjectBrowser.vue: batch toolbar buttons, context menu, paste dialog - Refactor TreeClipboard type to support multi-table entries - Add i18n keys for all 7 locale files (en, zh-CN, zh-TW, ja, es, it, pt-BR) * Fix table paste shortcuts * Fix Kafka MQ sidebar detection * Restrict table paste scope * Support data copy for primary SQL databases
This commit is contained in:
1 parent
5d2ce9f189
commit
0c91fecf5d
26 files changed
+943
-34
No files matched your search
@@ -116,6 +116,23 @@ pub struct DuplicateTableStructureSqlOptions {
|
||||
pub target_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CopyTableDataSqlOptions {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub database_type: Option<DatabaseType>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub schema: Option<String>,
|
||||
pub source_name: String,
|
||||
pub target_name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub columns: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub postgres_overriding_system_value: bool,
|
||||
#[serde(default)]
|
||||
pub sqlserver_identity_insert: bool,
|
||||
}
|
||||
|
||||
const MYSQL_COMPATIBLE_PROFILES: &[&str] =
|
||||
&["mysql", "mariadb", "tidb", "oceanbase", "doris", "starrocks", "custom_mysql"];
|
||||
|
||||
@@ -309,6 +326,32 @@ pub fn build_duplicate_table_structure_sql(options: DuplicateTableStructureSqlOp
|
||||
format!("CREATE TABLE {target} AS SELECT * FROM {source} WHERE 0;")
|
||||
}
|
||||
|
||||
pub fn build_copy_table_data_sql(options: CopyTableDataSqlOptions) -> String {
|
||||
let source = qualified_name(options.database_type, options.schema.as_deref(), &options.source_name);
|
||||
let target = qualified_name(options.database_type, options.schema.as_deref(), &options.target_name);
|
||||
let Some(columns) = options.columns.filter(|columns| !columns.is_empty()) else {
|
||||
return format!("INSERT INTO {target} SELECT * FROM {source};");
|
||||
};
|
||||
let column_list = columns
|
||||
.iter()
|
||||
.map(|column| quote_table_identifier(options.database_type, column))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let postgres_override = if options.postgres_overriding_system_value
|
||||
&& matches!(options.database_type, Some(DatabaseType::Postgres | DatabaseType::Gaussdb | DatabaseType::Kwdb))
|
||||
{
|
||||
" OVERRIDING SYSTEM VALUE"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let insert_sql =
|
||||
format!("INSERT INTO {target} ({column_list}){postgres_override} SELECT {column_list} FROM {source};");
|
||||
if options.sqlserver_identity_insert && options.database_type == Some(DatabaseType::SqlServer) {
|
||||
return format!("SET IDENTITY_INSERT {target} ON;\n{insert_sql}\nSET IDENTITY_INSERT {target} OFF;");
|
||||
}
|
||||
insert_sql
|
||||
}
|
||||
|
||||
pub fn supports_object_rename(database_type: Option<DatabaseType>, object_type: DatabaseObjectType) -> bool {
|
||||
let Some(database_type) = database_type else {
|
||||
return false;
|
||||
@@ -766,6 +809,15 @@ mod tests {
|
||||
}),
|
||||
"CREATE TABLE `users_copy` LIKE `users`;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_duplicate_table_structure_sql(DuplicateTableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Postgres),
|
||||
schema: Some("public".to_string()),
|
||||
source_name: "users".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
}),
|
||||
"CREATE TABLE \"public\".\"users_copy\" (LIKE \"public\".\"users\" INCLUDING ALL);"
|
||||
);
|
||||
assert_eq!(
|
||||
build_duplicate_table_structure_sql(DuplicateTableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Kwdb),
|
||||
@@ -813,6 +865,58 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_copy_table_data_sql() {
|
||||
assert_eq!(
|
||||
build_copy_table_data_sql(CopyTableDataSqlOptions {
|
||||
database_type: Some(DatabaseType::Sqlite),
|
||||
schema: None,
|
||||
source_name: "users".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
columns: None,
|
||||
postgres_overriding_system_value: false,
|
||||
sqlserver_identity_insert: false,
|
||||
}),
|
||||
"INSERT INTO \"users_copy\" SELECT * FROM \"users\";"
|
||||
);
|
||||
assert_eq!(
|
||||
build_copy_table_data_sql(CopyTableDataSqlOptions {
|
||||
database_type: Some(DatabaseType::Mysql),
|
||||
schema: None,
|
||||
source_name: "users".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
columns: Some(vec!["id".to_string(), "name".to_string()]),
|
||||
postgres_overriding_system_value: false,
|
||||
sqlserver_identity_insert: false,
|
||||
}),
|
||||
"INSERT INTO `users_copy` (`id`, `name`) SELECT `id`, `name` FROM `users`;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_copy_table_data_sql(CopyTableDataSqlOptions {
|
||||
database_type: Some(DatabaseType::Postgres),
|
||||
schema: Some("public".to_string()),
|
||||
source_name: "users".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
columns: Some(vec!["id".to_string(), "name".to_string()]),
|
||||
postgres_overriding_system_value: true,
|
||||
sqlserver_identity_insert: false,
|
||||
}),
|
||||
"INSERT INTO \"public\".\"users_copy\" (\"id\", \"name\") OVERRIDING SYSTEM VALUE SELECT \"id\", \"name\" FROM \"public\".\"users\";"
|
||||
);
|
||||
assert_eq!(
|
||||
build_copy_table_data_sql(CopyTableDataSqlOptions {
|
||||
database_type: Some(DatabaseType::SqlServer),
|
||||
schema: Some("dbo".to_string()),
|
||||
source_name: "users".to_string(),
|
||||
target_name: "users_copy".to_string(),
|
||||
columns: Some(vec!["id".to_string(), "name".to_string()]),
|
||||
postgres_overriding_system_value: false,
|
||||
sqlserver_identity_insert: true,
|
||||
}),
|
||||
"SET IDENTITY_INSERT [dbo].[users_copy] ON;\nINSERT INTO [dbo].[users_copy] ([id], [name]) SELECT [id], [name] FROM [dbo].[users];\nSET IDENTITY_INSERT [dbo].[users_copy] OFF;"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_mysql_table_and_view_rename_sql() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in new issue
Block a user