Update vector columns logic

This commit is contained in:
2026-08-05 13:28:57 +01:00
parent 4f203ce4e7
commit c97bedc271
7 changed files with 142 additions and 217 deletions
+57 -20
View File
@@ -4,7 +4,6 @@ import getTableColumns, {} from "./get-table-columns";
import isVectorField from "./is-vector-field";
import mapDataType from "./map-data-types";
import MariaDBQuoteGen from "./mariadb-quote-gen";
import recreateTable from "./recreate-table";
import runSchemaQuery, { querySchemaRows } from "./run-schema-query";
import schemaCondition from "./schema-condition";
import { dropForeignKeysOnColumns } from "./sync-foreign-keys";
@@ -141,6 +140,50 @@ async function dropColumn({ tableName, fieldName, config, }) {
config,
});
}
/**
* Drop + re-add a column (values discarded). Used when VECTOR dimensions change —
* MODIFY cannot resize VECTOR, and a full table rebuild is unnecessary.
*/
async function recreateColumn({ tableName, field, config, }) {
if (!field.fieldName)
return;
console.log(`Recreating column: ${tableName}.${field.fieldName} (values will be cleared)`);
await dropForeignKeysOnColumns({
tableName,
columns: [field.fieldName],
config,
});
const schemaCond = schemaCondition(config);
const indexRows = await querySchemaRows({
query: `SELECT INDEX_NAME, COLUMN_NAME FROM information_schema.STATISTICS WHERE ${schemaCond.where} AND TABLE_NAME = ? AND INDEX_NAME <> 'PRIMARY'`,
values: [...schemaCond.values, tableName],
config,
});
const indexesToDrop = new Set();
for (const row of indexRows) {
if (row.COLUMN_NAME === field.fieldName) {
indexesToDrop.add(row.INDEX_NAME);
}
}
for (const indexName of indexesToDrop) {
console.log(`Dropping index ${indexName} because column ${field.fieldName} is being recreated`);
try {
await runSchemaQuery({
query: `DROP INDEX ${MariaDBQuoteGen(indexName)} ON ${MariaDBQuoteGen(tableName)}`,
config,
});
}
catch (err) {
if (String(err?.message || "").includes("needed in a foreign key constraint")) {
console.warn(`Skipping drop of index ${indexName}: required by a foreign key constraint`);
continue;
}
throw err;
}
}
await dropColumn({ tableName, fieldName: field.fieldName, config });
await addColumn({ tableName, field, config });
}
export default async function updateTable({ table, config, }) {
const existingColumns = await getTableColumns({
tableName: table.tableName,
@@ -154,8 +197,8 @@ export default async function updateTable({ table, config, }) {
const codeFieldsMap = new Map((table.fields || []).map((f) => [f.fieldName, f]));
const fieldsToAdd = [];
const fieldsToModify = [];
const fieldsToRecreate = [];
const fieldsToDrop = [];
let needsVectorRecreate = false;
for (const field of table.fields || []) {
if (!field.fieldName)
continue;
@@ -168,7 +211,9 @@ export default async function updateTable({ table, config, }) {
if (isVectorField(field)) {
typeDiverged = vectorTypeDiverged(liveField.type, liveField.comment || "", field);
if (typeDiverged) {
needsVectorRecreate = true;
// VECTOR dimensions / storage cannot be MODIFYed — drop + re-add column
fieldsToRecreate.push(field);
continue;
}
}
const attrsDiverged = !typeDiverged && columnAttributesDiverged(liveField, field);
@@ -177,12 +222,6 @@ export default async function updateTable({ table, config, }) {
}
}
}
// Vector dimension / storage type changes → full rebuild automatically
if (needsVectorRecreate) {
console.log(`Vector column change detected on \`${table.tableName}\`; recreating table`);
await recreateTable({ table, config });
return;
}
for (const col of existingColumns) {
if (!codeFieldsMap.has(col.name)) {
fieldsToDrop.push(col.name);
@@ -190,6 +229,7 @@ export default async function updateTable({ table, config, }) {
}
if (fieldsToAdd.length === 0 &&
fieldsToModify.length === 0 &&
fieldsToRecreate.length === 0 &&
fieldsToDrop.length === 0) {
return;
}
@@ -257,17 +297,14 @@ export default async function updateTable({ table, config, }) {
await addColumn({ tableName: table.tableName, field, config });
}
for (const field of fieldsToModify) {
try {
await modifyColumn({ tableName: table.tableName, field, config });
}
catch (err) {
if (isVectorField(field)) {
console.warn(`[Vector Resize] Re-aligning dimension spaces natively via safe migration schema rebuild.`);
await recreateTable({ table, config });
return;
}
throw err;
}
await modifyColumn({ tableName: table.tableName, field, config });
}
for (const field of fieldsToRecreate) {
await recreateColumn({
tableName: table.tableName,
field,
config,
});
}
for (const fieldName of fieldsToDrop) {
await dropColumn({ tableName: table.tableName, fieldName, config });