feat: add index type, included columns, filter, and comment support

- Add filter, index_type, included_columns, and comment fields to IndexInfo
- PostgreSQL: query pg_am for index type, use indnkeyatts to split key/included columns, retrieve index comments via obj_description()
- SQL Server: add type_desc, filter_definition, included columns via is_included_column
- MySQL: add INDEX_TYPE from information_schema.STATISTICS
- Oracle: add INDEX_TYPE from ALL_INDEXES
- DDL generation: add USING (PostgreSQL), type prefix + INCLUDE + WHERE (SQL Server), COMMENT ON INDEX (PostgreSQL)
- Frontend: update types, state mapping, SQL generation, and i18n translations
This commit is contained in:
yavon007
2026-05-03 20:10:35 +08:00
parent b88e1b8ccb
commit 4e1b35186b
15 changed files with 114 additions and 29 deletions
+5
View File
@@ -335,7 +335,12 @@ export default {
actions: "Actions",
indexName: "Index",
indexColumns: "Columns",
indexColumnsPlaceholder: "Select columns...",
unique: "Unique",
indexType: "Type",
includedColumns: "Included",
includedColumnsPlaceholder: "Select columns...",
filter: "Filter",
primary: "Primary",
drop: "Drop",
restore: "Restore",
+5
View File
@@ -335,7 +335,12 @@ export default {
actions: "操作",
indexName: "索引名",
indexColumns: "字段列表",
indexColumnsPlaceholder: "选择列...",
unique: "唯一",
indexType: "类型",
includedColumns: "包含列",
includedColumnsPlaceholder: "选择列...",
filter: "过滤条件",
primary: "主键",
drop: "删除",
restore: "恢复",
+7 -1
View File
@@ -180,7 +180,13 @@ export function generateSyncSql(
if (idx.type === "added" && idx.source) {
const cols = idx.source.columns.map((c) => quoteId(c, dbType)).join(", ");
const unique = idx.source.is_unique ? "UNIQUE " : "";
lines.push(`CREATE ${unique}INDEX ${quoteId(idx.name, dbType)} ON ${qt} (${cols});`);
const idxType = idx.source.index_type ?? "";
const usingClause = idxType && dbType === "postgres" ? ` USING ${idxType}` : "";
const typePrefix = idxType && dbType === "sqlserver" ? `${idxType} ` : "";
const incCols = idx.source.included_columns ?? [];
const includeClause = incCols.length > 0 ? ` INCLUDE (${incCols.map((c) => quoteId(c, dbType)).join(", ")})` : "";
const filter = idx.source.filter ? ` WHERE ${idx.source.filter}` : "";
lines.push(`CREATE ${unique}${typePrefix}INDEX ${quoteId(idx.name, dbType)} ON ${qt}${usingClause} (${cols})${includeClause}${filter};`);
} else if (idx.type === "removed") {
if (isMySQL) {
lines.push(`DROP INDEX ${quoteId(idx.name, dbType)} ON ${qt};`);
+16 -1
View File
@@ -18,6 +18,10 @@ export interface EditableStructureIndex {
columns: string[];
isUnique: boolean;
isPrimary: boolean;
filter: string;
indexType: string;
includedColumns: string[];
comment: string;
original?: IndexInfo;
markedForDrop: boolean;
}
@@ -245,7 +249,18 @@ function buildIndexSql(options: BuildTableStructureChangeSqlOptions, warnings: s
if (!name || columns.length === 0) continue;
const unique = index.isUnique ? "UNIQUE " : "";
const cols = columns.map((column) => quoteIdent(databaseType, column)).join(", ");
statements.push(`CREATE ${unique}INDEX ${quoteIdent(databaseType, name)} ON ${table} (${cols});`);
const idxType = clean(index.indexType);
const usingClause = idxType && databaseType === "postgres" ? ` USING ${idxType}` : "";
const typePrefix = idxType && databaseType === "sqlserver" ? `${idxType} ` : "";
const incCols = index.includedColumns.map(clean).filter(Boolean);
const includeClause = incCols.length > 0 ? ` INCLUDE (${incCols.map((c) => quoteIdent(databaseType, c)).join(", ")})` : "";
const filter = clean(index.filter);
const whereClause = filter ? ` WHERE ${filter}` : "";
statements.push(`CREATE ${unique}${typePrefix}INDEX ${quoteIdent(databaseType, name)} ON ${table}${usingClause} (${cols})${includeClause}${whereClause};`);
const comment = clean(index.comment);
if (comment && databaseType === "postgres") {
statements.push(`COMMENT ON INDEX ${quoteIdent(databaseType, name)} IS ${quoteString(comment)};`);
}
}
return statements;
+4 -7
View File
@@ -22,18 +22,15 @@ export function createIndexDrafts(indexes: IndexInfo[]): EditableStructureIndex[
columns: [...index.columns],
isUnique: index.is_unique,
isPrimary: index.is_primary,
filter: index.filter ?? "",
indexType: index.index_type ?? "",
includedColumns: index.included_columns ? [...index.included_columns] : [],
comment: index.comment ?? "",
original: index,
markedForDrop: false,
}));
}
export function splitIndexColumns(value: string): string[] {
return value
.split(/[,\s]+/g)
.map((part) => part.trim())
.filter(Boolean);
}
export function toColumnNames(columns: string[]): string {
return columns.join(", ");
}
+5
View File
@@ -44,6 +44,7 @@ export interface ColumnInfo {
comment?: string | null;
numeric_precision?: number | null;
numeric_scale?: number | null;
character_maximum_length?: number | null;
}
export interface IndexInfo {
@@ -51,6 +52,10 @@ export interface IndexInfo {
columns: string[];
is_unique: boolean;
is_primary: boolean;
filter?: string | null;
index_type?: string | null;
included_columns?: string[] | null;
comment?: string | null;
}
export interface ForeignKeyInfo {