mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-23 16:04:26 +00:00
@@ -36,7 +36,7 @@ import { getPostgresDataTypeHelp } from "@/lib/table/postgresDataTypeHelp";
|
||||
import { getSqliteDataTypeHelp } from "@/lib/table/sqliteDataTypeHelp";
|
||||
import { getTableMetadataCapabilities, firstStructureMetadataTab, isStructureMetadataTabSupported } from "@/lib/table/tableMetadataCapabilities";
|
||||
import { hasTableStructureRefreshWork, unloadedTableStructureRefreshScope, visibleTableStructureRefreshScope, type TableStructureRefreshScope } from "@/lib/table/tableStructureMetadataLoading";
|
||||
import { canAddTableStructureColumn, getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
|
||||
import { canAddTableStructureColumn, getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, sanitizeStructureIndexesForCapabilities, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
|
||||
import { orderedColumnIndexes, uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
|
||||
import { loadTableDataGridColumnOrder, notifyTableDataGridColumnOrderChanged, removeTableDataGridColumnOrder, saveTableDataGridColumnOrder, tableDataGridColumnOrderScopeKey } from "@/lib/dataGrid/dataGridColumnLayoutStorage";
|
||||
import { connectionObjectTreeQuerySchema, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
@@ -620,7 +620,7 @@ function onIndexColResize(e: MouseEvent, col: number) {
|
||||
const connection = computed(() => (props.connectionId ? store.getConfig(props.connectionId) : undefined));
|
||||
const databaseType = computed(() => tableStructureDatabaseTypeForConnection(connection.value));
|
||||
const usesMysql8SafeDefaults = computed(() => databaseType.value === "mysql" && connection.value?.db_type === "mysql" && connection.value.driver_profile === "mysql");
|
||||
const structureCapabilities = computed(() => getTableStructureCapabilities(databaseType.value, connection.value?.db_type));
|
||||
const structureCapabilities = computed(() => getTableStructureCapabilities(databaseType.value, connection.value?.db_type, connection.value?.database_info?.productVersion));
|
||||
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(databaseType.value));
|
||||
const structureDialect = computed(() => structureCapabilities.value.dialect);
|
||||
const isTableCommentDisabled = computed(() => !structureCapabilities.value.comment);
|
||||
@@ -1143,7 +1143,7 @@ function structureChangeOptions(): BuildTableStructureChangeSqlOptions {
|
||||
schema: props.schema,
|
||||
tableName: isCreateMode.value ? newTableName.value.trim() : props.tableName || "",
|
||||
columns: columns.value,
|
||||
indexes: indexes.value,
|
||||
indexes: sanitizeStructureIndexesForCapabilities(indexes.value, structureCapabilities.value),
|
||||
foreignKeys: foreignKeys.value,
|
||||
triggers: triggers.value,
|
||||
tableComment: tableComment.value,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
|
||||
import { getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, sanitizeStructureIndexesForCapabilities, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
|
||||
|
||||
describe("tableStructureCapabilities", () => {
|
||||
it("uses table rebuilds only for native SQLite connections", () => {
|
||||
@@ -28,6 +28,40 @@ describe("tableStructureCapabilities", () => {
|
||||
expect(getTableStructureCapabilities("postgres", "postgres").alterStrategy).toBe("direct");
|
||||
});
|
||||
|
||||
it("enables PostgreSQL index INCLUDE only for known supported versions", () => {
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", "PostgreSQL 10.15 (Transwarp) on x86_64-pc-linux-gnu").indexInclude).toBe(false);
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", "9.6.24").indexInclude).toBe(false);
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", "11.0").indexInclude).toBe(true);
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", "PostgreSQL 16.14").indexInclude).toBe(true);
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", undefined).indexInclude).toBe(true);
|
||||
expect(getTableStructureCapabilities("postgres", "postgres", "unknown").indexInclude).toBe(true);
|
||||
expect(getTableStructureCapabilities("sqlserver", "sqlserver", "10.0").indexInclude).toBe(true);
|
||||
});
|
||||
|
||||
it("removes unsupported included columns before SQL generation", () => {
|
||||
const index = {
|
||||
id: "new:index",
|
||||
name: "example_idx",
|
||||
columns: ["key_column"],
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
filter: "",
|
||||
indexType: "BTREE",
|
||||
includedColumns: ["included_column"],
|
||||
comment: "",
|
||||
markedForDrop: false,
|
||||
};
|
||||
|
||||
const indexes = [index];
|
||||
const postgres10Indexes = sanitizeStructureIndexesForCapabilities(indexes, getTableStructureCapabilities("postgres", "postgres", "10.15"));
|
||||
expect(postgres10Indexes).not.toBe(indexes);
|
||||
expect(postgres10Indexes[0].includedColumns).toEqual([]);
|
||||
expect(index.includedColumns).toEqual(["included_column"]);
|
||||
|
||||
const postgres11Indexes = sanitizeStructureIndexesForCapabilities(indexes, getTableStructureCapabilities("postgres", "postgres", "11.0"));
|
||||
expect(postgres11Indexes).toBe(indexes);
|
||||
});
|
||||
|
||||
it("disables persisted comment editing for IRIS without disabling other structure changes", () => {
|
||||
expect(getTableStructureCapabilities("iris", "iris")).toMatchObject({
|
||||
comment: false,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
import type { EditableStructureIndex } from "@/lib/table/tableStructureEditorSql";
|
||||
|
||||
export type TableStructureDialect = "mysql" | "postgres" | "sqlite" | "duckdb" | "sqlserver" | "oracle" | "h2" | "clickhouse" | "informix" | "influxdb" | "unsupported";
|
||||
export type TableStructureAlterStrategy = "none" | "direct" | "sqlite-rebuild";
|
||||
@@ -111,6 +112,11 @@ const postgresCapabilities = capabilities({
|
||||
foreignKey: true,
|
||||
});
|
||||
|
||||
const postgresBefore11Capabilities = capabilities({
|
||||
...postgresCapabilities,
|
||||
indexInclude: false,
|
||||
});
|
||||
|
||||
const redshiftCapabilities = capabilities({
|
||||
...postgresCapabilities,
|
||||
createIndex: false,
|
||||
@@ -343,11 +349,29 @@ const capabilityByType: Partial<Record<DatabaseType, TableStructureCapabilities>
|
||||
manticoresearch: manticoreSearchCapabilities,
|
||||
};
|
||||
|
||||
export function getTableStructureCapabilities(dbType?: DatabaseType, connectionDbType?: DatabaseType): TableStructureCapabilities {
|
||||
function postgresMajorVersion(productVersion?: string): number | undefined {
|
||||
const normalized = productVersion?.trim();
|
||||
if (!normalized) return undefined;
|
||||
const match = normalized.match(/\bPostgreSQL\s+(\d+)(?:\.\d+)?\b/i) ?? normalized.match(/^(\d+)(?:\.\d+)?\b/);
|
||||
if (!match) return undefined;
|
||||
const majorVersion = Number.parseInt(match[1], 10);
|
||||
return Number.isFinite(majorVersion) ? majorVersion : undefined;
|
||||
}
|
||||
|
||||
export function getTableStructureCapabilities(dbType?: DatabaseType, connectionDbType?: DatabaseType, productVersion?: string): TableStructureCapabilities {
|
||||
if (dbType === "sqlite" && connectionDbType === "sqlite") return nativeSqliteCapabilities;
|
||||
if (dbType === "postgres") {
|
||||
const majorVersion = postgresMajorVersion(productVersion);
|
||||
if (majorVersion !== undefined && majorVersion < 11) return postgresBefore11Capabilities;
|
||||
}
|
||||
return dbType ? (capabilityByType[dbType] ?? unsupportedCapabilities) : unsupportedCapabilities;
|
||||
}
|
||||
|
||||
export function sanitizeStructureIndexesForCapabilities(indexes: EditableStructureIndex[], capabilities: Pick<TableStructureCapabilities, "indexInclude">): EditableStructureIndex[] {
|
||||
if (capabilities.indexInclude || indexes.every((index) => index.includedColumns.length === 0)) return indexes;
|
||||
return indexes.map((index) => (index.includedColumns.length === 0 ? index : { ...index, includedColumns: [] }));
|
||||
}
|
||||
|
||||
export function canEditTableStructure(dbType?: DatabaseType): boolean {
|
||||
const caps = getTableStructureCapabilities(dbType);
|
||||
return caps.createTable || caps.addColumn || caps.alterExistingColumn || caps.createIndex || caps.dropIndex;
|
||||
|
||||
Reference in New Issue
Block a user