112 lines
4.4 KiB
JavaScript
112 lines
4.4 KiB
JavaScript
import createTable from "./create-table";
|
|
import getTableColumns from "./get-table-columns";
|
|
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
|
import runSchemaQuery, { querySchemaRows } from "./run-schema-query";
|
|
import schemaCondition from "./schema-condition";
|
|
async function checkIfTableExists({ tableName, config, }) {
|
|
const schemaCond = schemaCondition(config);
|
|
const rows = await querySchemaRows({
|
|
query: `SELECT 1 AS \`table_exists\` FROM information_schema.TABLES WHERE ${schemaCond.where} AND TABLE_NAME = ? LIMIT 1`,
|
|
values: [...schemaCond.values, tableName],
|
|
config,
|
|
});
|
|
return Boolean(rows[0]?.table_exists);
|
|
}
|
|
/**
|
|
* Full table rebuild. For `isVector` tables this drops and recreates in place
|
|
* (preserving rows when possible). For regular tables it uses a temp-table swap.
|
|
*/
|
|
export default async function recreateTable({ table, config, }) {
|
|
const doesTableExist = await checkIfTableExists({
|
|
tableName: table.tableName,
|
|
config,
|
|
});
|
|
if (!doesTableExist) {
|
|
await createTable({ table, config });
|
|
return;
|
|
}
|
|
/**
|
|
* Vector tables: drop + recreate + reinsert (MariaDB VECTOR INDEX / dim
|
|
* changes are not reliably alterable in place).
|
|
*/
|
|
if (table.isVector) {
|
|
console.log(`Recreating vector table: ${table.tableName}`);
|
|
const existingRows = await querySchemaRows({
|
|
query: `SELECT * FROM ${MariaDBQuoteGen(table.tableName)}`,
|
|
config,
|
|
});
|
|
await runSchemaQuery({ query: `SET FOREIGN_KEY_CHECKS = 0`, config });
|
|
try {
|
|
await runSchemaQuery({
|
|
query: `DROP TABLE IF EXISTS ${MariaDBQuoteGen(table.tableName)}`,
|
|
config,
|
|
});
|
|
await createTable({ table, config });
|
|
}
|
|
finally {
|
|
await runSchemaQuery({ query: `SET FOREIGN_KEY_CHECKS = 1`, config });
|
|
}
|
|
if (existingRows.length > 0) {
|
|
const schemaFieldNames = new Set((table.fields || [])
|
|
.map((f) => f.fieldName)
|
|
.filter((n) => Boolean(n)));
|
|
for (const row of existingRows) {
|
|
const columns = Object.keys(row).filter((c) => schemaFieldNames.has(c));
|
|
if (columns.length === 0)
|
|
continue;
|
|
const placeholders = columns.map(() => "?").join(", ");
|
|
const columnList = columns
|
|
.map((c) => MariaDBQuoteGen(c))
|
|
.join(", ");
|
|
await runSchemaQuery({
|
|
query: `INSERT INTO ${MariaDBQuoteGen(table.tableName)} (${columnList}) VALUES (${placeholders})`,
|
|
values: columns.map((c) => row[c] ?? null),
|
|
config,
|
|
});
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
const tempTableName = `${table.tableName}_temp_${Date.now()}`;
|
|
const backupOldTableName = `${table.tableName}_old_${Date.now()}`;
|
|
const existingColumns = await getTableColumns({
|
|
tableName: table.tableName,
|
|
config,
|
|
});
|
|
const columnsToKeep = (table.fields || [])
|
|
.filter((field) => existingColumns.some((column) => column.name === field.fieldName))
|
|
.map((field) => field.fieldName)
|
|
.filter((fieldName) => Boolean(fieldName));
|
|
await createTable({
|
|
table: { ...table, tableName: tempTableName },
|
|
config,
|
|
});
|
|
if (columnsToKeep.length > 0) {
|
|
const columnList = columnsToKeep
|
|
.map((column) => MariaDBQuoteGen(column))
|
|
.join(", ");
|
|
await runSchemaQuery({
|
|
query: `INSERT INTO ${MariaDBQuoteGen(tempTableName)} (${columnList}) SELECT ${columnList} FROM ${MariaDBQuoteGen(table.tableName)}`,
|
|
config,
|
|
});
|
|
}
|
|
await runSchemaQuery({ query: `SET FOREIGN_KEY_CHECKS = 0`, config });
|
|
try {
|
|
await runSchemaQuery({
|
|
query: `RENAME TABLE ${MariaDBQuoteGen(table.tableName)} TO ${MariaDBQuoteGen(backupOldTableName)}`,
|
|
config,
|
|
});
|
|
await runSchemaQuery({
|
|
query: `RENAME TABLE ${MariaDBQuoteGen(tempTableName)} TO ${MariaDBQuoteGen(table.tableName)}`,
|
|
config,
|
|
});
|
|
await runSchemaQuery({
|
|
query: `DROP TABLE ${MariaDBQuoteGen(backupOldTableName)}`,
|
|
config,
|
|
});
|
|
}
|
|
finally {
|
|
await runSchemaQuery({ query: `SET FOREIGN_KEY_CHECKS = 1`, config });
|
|
}
|
|
}
|