feat(sqlite): support table structure changes
This commit is contained in:
1 parent
f7eec00c8e
commit
fc8d407a62
25 files changed
+2320
-50
No files matched your search
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
|
||||
|
||||
describe("tableStructureCapabilities", () => {
|
||||
it("uses table rebuilds only for native SQLite connections", () => {
|
||||
expect(getTableStructureCapabilities("sqlite", "sqlite")).toMatchObject({
|
||||
alterStrategy: "sqlite-rebuild",
|
||||
alterExistingColumn: true,
|
||||
alterType: true,
|
||||
});
|
||||
|
||||
for (const [databaseType, connectionType] of [
|
||||
["rqlite", "rqlite"],
|
||||
["turso", "turso"],
|
||||
["sqlite", "jdbc"],
|
||||
["sqlite", undefined],
|
||||
] as const) {
|
||||
expect(getTableStructureCapabilities(databaseType, connectionType)).toMatchObject({
|
||||
alterStrategy: "none",
|
||||
alterExistingColumn: false,
|
||||
alterType: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("marks databases with native ALTER COLUMN support as direct", () => {
|
||||
expect(getTableStructureCapabilities("mysql", "mysql").alterStrategy).toBe("direct");
|
||||
expect(getTableStructureCapabilities("postgres", "postgres").alterStrategy).toBe("direct");
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
combineDataTypeForDatabase,
|
||||
createColumnDrafts,
|
||||
dataTypeLengthInputValue,
|
||||
getDefaultLengthForType,
|
||||
hasExistingColumnTypeChange,
|
||||
isDataTypeLengthDisabled,
|
||||
isMysqlCharacterDataType,
|
||||
isMysqlEnumDataType,
|
||||
@@ -96,6 +98,40 @@ describe("tableStructureEditorState", () => {
|
||||
expect(combineDataTypeForDatabase("mysql", "integer", "11")).toBe("integer(11)");
|
||||
});
|
||||
|
||||
it("does not add MySQL display lengths when choosing SQLite-family types", () => {
|
||||
for (const databaseType of ["sqlite", "rqlite", "turso"] as const) {
|
||||
expect(getDefaultLengthForType(databaseType, "integer")).toBe("");
|
||||
expect(getDefaultLengthForType(databaseType, "real")).toBe("");
|
||||
expect(combineDataTypeForDatabase(databaseType, "integer", getDefaultLengthForType(databaseType, "integer"))).toBe("integer");
|
||||
}
|
||||
|
||||
expect(getDefaultLengthForType("mysql", "integer")).toBe("11");
|
||||
});
|
||||
|
||||
it("requires a SQLite rebuild only for a retained existing column type change", () => {
|
||||
const [column] = createColumnDrafts(
|
||||
[
|
||||
{
|
||||
name: "status",
|
||||
data_type: "integer",
|
||||
is_nullable: false,
|
||||
column_default: null,
|
||||
is_primary_key: false,
|
||||
extra: null,
|
||||
},
|
||||
],
|
||||
"sqlite",
|
||||
);
|
||||
|
||||
expect(hasExistingColumnTypeChange([column])).toBe(false);
|
||||
column.name = "state";
|
||||
expect(hasExistingColumnTypeChange([column])).toBe(false);
|
||||
column.dataType = "text";
|
||||
expect(hasExistingColumnTypeChange([column])).toBe(true);
|
||||
column.markedForDrop = true;
|
||||
expect(hasExistingColumnTypeChange([column])).toBe(false);
|
||||
});
|
||||
|
||||
it("strips SQL Server metadata parentheses from editable defaults", () => {
|
||||
const drafts = createColumnDrafts(
|
||||
[
|
||||
|
||||
@@ -185,6 +185,8 @@ export const buildEditableObjectSource = forward("buildEditableObjectSource");
|
||||
export const buildRoutineRenameObjectSourceStatements = forward("buildRoutineRenameObjectSourceStatements");
|
||||
export const buildViewDdlSql = forward("buildViewDdlSql");
|
||||
export const buildTableStructureChangeSql = forward("buildTableStructureChangeSql");
|
||||
export const previewSqliteTableStructureChange = forward("previewSqliteTableStructureChange");
|
||||
export const applySqliteTableStructureChange = forward("applySqliteTableStructureChange");
|
||||
export const buildCreateTableSql = forward("buildCreateTableSql");
|
||||
export const buildSingleColumnAlterSql = forward("buildSingleColumnAlterSql");
|
||||
export const analyzeEditableQueryEditability = forward("analyzeEditableQueryEditability");
|
||||
|
||||
@@ -112,7 +112,7 @@ import type {
|
||||
DataGridSaveStatementOptions,
|
||||
HiveTablePropertiesSqlOptions,
|
||||
} from "@/lib/dataGrid/dataGridSql";
|
||||
import type { BuildTableStructureChangeSqlOptions, BuildSingleColumnAlterSqlOptions, TableStructureChangeSql } from "@/lib/table/tableStructureEditorSql";
|
||||
import type { BuildTableStructureChangeSqlOptions, BuildSingleColumnAlterSqlOptions, SqliteTableStructureChangePreview, TableStructureChangeSql } from "@/lib/table/tableStructureEditorSql";
|
||||
import type { BuildTableSelectSqlOptions } from "@/lib/table/tableSelectSql";
|
||||
import type { DatabaseSearchSql, DatabaseSearchSqlOptions, SearchResultWhereOptions } from "@/lib/database/databaseSearch";
|
||||
import type { BuildEditableObjectSourceSqlInput, BuildRoutineRenameObjectSourceInput } from "@/lib/table/objectSourceEditor";
|
||||
@@ -855,6 +855,14 @@ export async function buildTableStructureChangeSql(options: BuildTableStructureC
|
||||
return post("/api/query/build-table-structure-change-sql", { options });
|
||||
}
|
||||
|
||||
export async function previewSqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions): Promise<SqliteTableStructureChangePreview> {
|
||||
return post("/api/query/preview-sqlite-table-structure-change", { connectionId, database, options });
|
||||
}
|
||||
|
||||
export async function applySqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions, schemaRevision: string): Promise<QueryResult> {
|
||||
return post("/api/query/apply-sqlite-table-structure-change", { connectionId, database, options, schemaRevision });
|
||||
}
|
||||
|
||||
export async function buildCreateTableSql(options: BuildTableStructureChangeSqlOptions): Promise<TableStructureChangeSql> {
|
||||
return post("/api/query/build-create-table-sql", { options });
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ import type {
|
||||
} from "@/lib/dataGrid/dataGridSql";
|
||||
import type { DataCompareFromTablesOptions, DataCompareFromTablesPreparation, DataCompareSyncPlan, DataCompareSyncPlanOptions, DataComparePreparation, DataComparePreparationOptions } from "@/lib/dataGrid/dataCompare";
|
||||
import type { SchemaDiffPreparation, SchemaDiffPreparationOptions, TableDiff, FunctionDiff, SequenceDiff, RuleDiff, OwnerDiff } from "@/lib/schema/schemaDiff";
|
||||
import type { BuildTableStructureChangeSqlOptions, BuildSingleColumnAlterSqlOptions, TableStructureChangeSql } from "@/lib/table/tableStructureEditorSql";
|
||||
import type { BuildTableStructureChangeSqlOptions, BuildSingleColumnAlterSqlOptions, SqliteTableStructureChangePreview, TableStructureChangeSql } from "@/lib/table/tableStructureEditorSql";
|
||||
import type { BuildTableSelectSqlOptions } from "@/lib/table/tableSelectSql";
|
||||
import type { DatabaseSearchSql, DatabaseSearchSqlOptions, SearchResultWhereOptions } from "@/lib/database/databaseSearch";
|
||||
import type { BuildEditableObjectSourceSqlInput, BuildRoutineRenameObjectSourceInput } from "@/lib/table/objectSourceEditor";
|
||||
@@ -946,6 +946,14 @@ export async function buildTableStructureChangeSql(options: BuildTableStructureC
|
||||
return invoke("build_table_structure_change_sql", { options });
|
||||
}
|
||||
|
||||
export async function previewSqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions): Promise<SqliteTableStructureChangePreview> {
|
||||
return invoke("preview_sqlite_table_structure_change", { connectionId, database, options });
|
||||
}
|
||||
|
||||
export async function applySqliteTableStructureChange(connectionId: string, database: string, options: BuildTableStructureChangeSqlOptions, schemaRevision: string): Promise<QueryResult> {
|
||||
return invoke("apply_sqlite_table_structure_change", { connectionId, database, options, schemaRevision });
|
||||
}
|
||||
|
||||
export async function buildCreateTableSql(options: BuildTableStructureChangeSqlOptions): Promise<TableStructureChangeSql> {
|
||||
return invoke("build_create_table_sql", { options });
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
export type TableStructureDialect = "mysql" | "postgres" | "sqlite" | "duckdb" | "sqlserver" | "oracle" | "h2" | "clickhouse" | "informix" | "influxdb" | "unsupported";
|
||||
export type TableStructureAlterStrategy = "none" | "direct" | "sqlite-rebuild";
|
||||
|
||||
export interface TableStructureCapabilities {
|
||||
dialect: TableStructureDialect;
|
||||
alterStrategy: TableStructureAlterStrategy;
|
||||
createTable: boolean;
|
||||
addColumn: boolean;
|
||||
dropColumn: boolean;
|
||||
@@ -27,6 +29,7 @@ export interface TableStructureCapabilities {
|
||||
|
||||
const unsupportedCapabilities: TableStructureCapabilities = {
|
||||
dialect: "unsupported",
|
||||
alterStrategy: "none",
|
||||
createTable: false,
|
||||
addColumn: false,
|
||||
dropColumn: false,
|
||||
@@ -49,7 +52,11 @@ const unsupportedCapabilities: TableStructureCapabilities = {
|
||||
};
|
||||
|
||||
function capabilities(overrides: Partial<TableStructureCapabilities>): TableStructureCapabilities {
|
||||
return { ...unsupportedCapabilities, ...overrides };
|
||||
const resolved = { ...unsupportedCapabilities, ...overrides };
|
||||
if (overrides.alterStrategy === undefined && resolved.alterExistingColumn) {
|
||||
resolved.alterStrategy = "direct";
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const mysqlCapabilities = capabilities({
|
||||
@@ -128,6 +135,13 @@ const sqliteCapabilities = capabilities({
|
||||
indexFilter: true,
|
||||
});
|
||||
|
||||
const nativeSqliteCapabilities = capabilities({
|
||||
...sqliteCapabilities,
|
||||
alterStrategy: "sqlite-rebuild",
|
||||
alterExistingColumn: true,
|
||||
alterType: true,
|
||||
});
|
||||
|
||||
const duckdbCapabilities = capabilities({
|
||||
dialect: "duckdb",
|
||||
createTable: true,
|
||||
@@ -315,7 +329,8 @@ const capabilityByType: Partial<Record<DatabaseType, TableStructureCapabilities>
|
||||
manticoresearch: manticoreSearchCapabilities,
|
||||
};
|
||||
|
||||
export function getTableStructureCapabilities(dbType?: DatabaseType): TableStructureCapabilities {
|
||||
export function getTableStructureCapabilities(dbType?: DatabaseType, connectionDbType?: DatabaseType): TableStructureCapabilities {
|
||||
if (dbType === "sqlite" && connectionDbType === "sqlite") return nativeSqliteCapabilities;
|
||||
return dbType ? (capabilityByType[dbType] ?? unsupportedCapabilities) : unsupportedCapabilities;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ export interface TableStructureChangeSql {
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface SqliteTableStructureChangePreview extends TableStructureChangeSql {
|
||||
schemaRevision: string;
|
||||
}
|
||||
|
||||
export interface BuildSingleColumnAlterSqlOptions {
|
||||
databaseType?: DatabaseType;
|
||||
schema?: string;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { ColumnInfo, DatabaseType, ForeignKeyInfo, IndexInfo, TriggerInfo } from "@/types/database.ts";
|
||||
import type { ColumnExtra, EditableStructureColumn, EditableStructureForeignKey, EditableStructureIndex, EditableStructureTrigger } from "@/lib/table/tableStructureEditorSql.ts";
|
||||
|
||||
export function hasExistingColumnTypeChange(columns: readonly EditableStructureColumn[]): boolean {
|
||||
return columns.some((column) => !!column.original && !column.markedForDrop && column.dataType !== column.original.data_type);
|
||||
}
|
||||
|
||||
export const DATA_TYPE_OPTIONS: Record<string, string[]> = {
|
||||
mysql: [
|
||||
"tinyint",
|
||||
@@ -938,7 +942,9 @@ function isValidTemporalPrecision(dbType: DatabaseType | undefined, params: stri
|
||||
|
||||
export function getDefaultLengthForType(_dbType: DatabaseType | undefined, baseType: string): string {
|
||||
const key = baseType.trim().toLowerCase();
|
||||
if (_dbType === "questdb") {
|
||||
if (_dbType === "sqlite" || _dbType === "rqlite" || _dbType === "turso") {
|
||||
return "";
|
||||
} else if (_dbType === "questdb") {
|
||||
return QUESTDB_TYPE_LENGTHS[key] ?? "";
|
||||
} else if (_dbType === "sqlserver") {
|
||||
return SQLSERVER_TYPE_LENGTHS[key] ?? "";
|
||||
|
||||
Reference in new issue
Block a user