Change table update strategy to full recreation every time

This commit is contained in:
Benjamin Toby 2026-04-05 07:10:26 +01:00
parent b46ff44dc7
commit 685d47d91f
5 changed files with 84 additions and 209 deletions

View File

@ -102,7 +102,9 @@ class SQLiteSchemaManager {
* Create a new table * Create a new table
*/ */
async createTable(table) { async createTable(table) {
if (!table.tableName.match(/_temp_\d+$/)) {
console.log(`Creating table: ${table.tableName}`); console.log(`Creating table: ${table.tableName}`);
}
let new_table = _.cloneDeep(table); let new_table = _.cloneDeep(table);
if (new_table.parentTableName) { if (new_table.parentTableName) {
const parent_table = this.db_schema.tables.find((t) => t.tableName === new_table.parentTableName); const parent_table = this.db_schema.tables.find((t) => t.tableName === new_table.parentTableName);
@ -148,27 +150,43 @@ class SQLiteSchemaManager {
*/ */
async updateTable(table) { async updateTable(table) {
console.log(`Updating table: ${table.tableName}`); console.log(`Updating table: ${table.tableName}`);
const existingColumns = this.getTableColumns(table.tableName); // const existingColumns = this.getTableColumns(table.tableName);
const schemaColumns = table.fields.map((f) => f.fieldName || ""); // const schemaColumns = table.fields.map((f) => f.fieldName || "");
// SQLite has limited ALTER TABLE support // // SQLite has limited ALTER TABLE support
// We need to use the recreation strategy for complex changes // // We need to use the recreation strategy for complex changes
const columnsToAdd = table.fields.filter((f) => f.fieldName && // const columnsToAdd = table.fields.filter(
!existingColumns.find((c) => c.name == f.fieldName && c.type == this.mapDataType(f))); // (f) =>
const columnsToRemove = existingColumns.filter((c) => !schemaColumns.includes(c.name)); // f.fieldName &&
const columnsToUpdate = table.fields.filter((f) => f.fieldName && // !existingColumns.find(
f.updatedField && // (c) =>
existingColumns.find((c) => c.name == f.fieldName && c.type == this.mapDataType(f))); // c.name == f.fieldName && c.type == this.mapDataType(f),
// Simple case: only adding columns // ),
if (columnsToRemove.length === 0 && columnsToUpdate.length === 0) { // );
for (const field of columnsToAdd) { // // const columnsToRemove = existingColumns.filter(
await this.addColumn(table.tableName, field); // // (c) => !schemaColumns.includes(c.name),
} // // );
} // // const columnsToUpdate = table.fields.filter(
else { // // (f) =>
// Complex case: need to recreate table // // f.fieldName &&
// // f.updatedField &&
// // existingColumns.find(
// // (c) =>
// // c.name == f.fieldName && c.type == this.mapDataType(f),
// // ),
// // );
// for (const field of columnsToAdd) {
// await this.addColumn(table.tableName, field);
// }
// // // Simple case: only adding columns
// // if (columnsToRemove.length === 0 && columnsToUpdate.length === 0) {
// // for (const field of columnsToAdd) {
// // await this.addColumn(table.tableName, field);
// // }
// // } else {
// // // Complex case: need to recreate table
// // }
await this.recreateTable(table); await this.recreateTable(table);
} }
}
/** /**
* Get existing columns for a table * Get existing columns for a table
*/ */
@ -382,79 +400,4 @@ class SQLiteSchemaManager {
this.db.close(); this.db.close();
} }
} }
// Example usage
async function main() {
const schema = {
dbName: "example_db",
tables: [
{
tableName: "users",
tableDescription: "User accounts",
fields: [
{
fieldName: "id",
dataType: "INTEGER",
primaryKey: true,
autoIncrement: true,
},
{
fieldName: "username",
dataType: "TEXT",
notNullValue: true,
unique: true,
},
{
fieldName: "email",
dataType: "TEXT",
notNullValue: true,
},
{
fieldName: "created_at",
dataType: "TEXT",
defaultValueLiteral: "CURRENT_TIMESTAMP",
},
],
indexes: [
{
indexName: "idx_users_email",
indexType: "regular",
indexTableFields: [
{ value: "email", dataType: "TEXT" },
],
},
],
},
{
tableName: "posts",
fields: [
{
fieldName: "id",
dataType: "INTEGER",
primaryKey: true,
autoIncrement: true,
},
{
fieldName: "user_id",
dataType: "INTEGER",
notNullValue: true,
foreignKey: {
destinationTableName: "users",
destinationTableColumnName: "id",
cascadeDelete: true,
},
},
{
fieldName: "title",
dataType: "TEXT",
notNullValue: true,
},
{
fieldName: "content",
dataType: "TEXT",
},
],
},
],
};
}
export { SQLiteSchemaManager }; export { SQLiteSchemaManager };

View File

@ -110,7 +110,6 @@ export type BUN_SQLITE_FieldSchemaType = {
fieldName?: string; fieldName?: string;
fieldDescription?: string; fieldDescription?: string;
originName?: string; originName?: string;
updatedField?: boolean;
dataType: (typeof BUN_SQLITE_DATATYPES)[number]["value"]; dataType: (typeof BUN_SQLITE_DATATYPES)[number]["value"];
nullValue?: boolean; nullValue?: boolean;
notNullValue?: boolean; notNullValue?: boolean;

View File

@ -1,6 +1,6 @@
{ {
"name": "@moduletrace/bun-sqlite", "name": "@moduletrace/bun-sqlite",
"version": "1.0.19", "version": "1.0.20",
"description": "SQLite manager for Bun", "description": "SQLite manager for Bun",
"author": "Benjamin Toby", "author": "Benjamin Toby",
"main": "dist/index.js", "main": "dist/index.js",

View File

@ -155,7 +155,9 @@ class SQLiteSchemaManager {
private async createTable( private async createTable(
table: BUN_SQLITE_TableSchemaType, table: BUN_SQLITE_TableSchemaType,
): Promise<void> { ): Promise<void> {
if (!table.tableName.match(/_temp_\d+$/)) {
console.log(`Creating table: ${table.tableName}`); console.log(`Creating table: ${table.tableName}`);
}
let new_table = _.cloneDeep(table); let new_table = _.cloneDeep(table);
@ -225,43 +227,50 @@ class SQLiteSchemaManager {
): Promise<void> { ): Promise<void> {
console.log(`Updating table: ${table.tableName}`); console.log(`Updating table: ${table.tableName}`);
const existingColumns = this.getTableColumns(table.tableName); // const existingColumns = this.getTableColumns(table.tableName);
const schemaColumns = table.fields.map((f) => f.fieldName || ""); // const schemaColumns = table.fields.map((f) => f.fieldName || "");
// SQLite has limited ALTER TABLE support // // SQLite has limited ALTER TABLE support
// We need to use the recreation strategy for complex changes // // We need to use the recreation strategy for complex changes
const columnsToAdd = table.fields.filter( // const columnsToAdd = table.fields.filter(
(f) => // (f) =>
f.fieldName && // f.fieldName &&
!existingColumns.find( // !existingColumns.find(
(c) => // (c) =>
c.name == f.fieldName && c.type == this.mapDataType(f), // c.name == f.fieldName && c.type == this.mapDataType(f),
), // ),
); // );
const columnsToRemove = existingColumns.filter(
(c) => !schemaColumns.includes(c.name), // // const columnsToRemove = existingColumns.filter(
); // // (c) => !schemaColumns.includes(c.name),
const columnsToUpdate = table.fields.filter( // // );
(f) =>
f.fieldName && // // const columnsToUpdate = table.fields.filter(
f.updatedField && // // (f) =>
existingColumns.find( // // f.fieldName &&
(c) => // // f.updatedField &&
c.name == f.fieldName && c.type == this.mapDataType(f), // // existingColumns.find(
), // // (c) =>
); // // c.name == f.fieldName && c.type == this.mapDataType(f),
// // ),
// // );
// for (const field of columnsToAdd) {
// await this.addColumn(table.tableName, field);
// }
// // // Simple case: only adding columns
// // if (columnsToRemove.length === 0 && columnsToUpdate.length === 0) {
// // for (const field of columnsToAdd) {
// // await this.addColumn(table.tableName, field);
// // }
// // } else {
// // // Complex case: need to recreate table
// // }
// Simple case: only adding columns
if (columnsToRemove.length === 0 && columnsToUpdate.length === 0) {
for (const field of columnsToAdd) {
await this.addColumn(table.tableName, field);
}
} else {
// Complex case: need to recreate table
await this.recreateTable(table); await this.recreateTable(table);
} }
}
/** /**
* Get existing columns for a table * Get existing columns for a table
@ -558,80 +567,4 @@ class SQLiteSchemaManager {
} }
} }
// Example usage
async function main() {
const schema: BUN_SQLITE_DatabaseSchemaType = {
dbName: "example_db",
tables: [
{
tableName: "users",
tableDescription: "User accounts",
fields: [
{
fieldName: "id",
dataType: "INTEGER",
primaryKey: true,
autoIncrement: true,
},
{
fieldName: "username",
dataType: "TEXT",
notNullValue: true,
unique: true,
},
{
fieldName: "email",
dataType: "TEXT",
notNullValue: true,
},
{
fieldName: "created_at",
dataType: "TEXT",
defaultValueLiteral: "CURRENT_TIMESTAMP",
},
],
indexes: [
{
indexName: "idx_users_email",
indexType: "regular",
indexTableFields: [
{ value: "email", dataType: "TEXT" },
],
},
],
},
{
tableName: "posts",
fields: [
{
fieldName: "id",
dataType: "INTEGER",
primaryKey: true,
autoIncrement: true,
},
{
fieldName: "user_id",
dataType: "INTEGER",
notNullValue: true,
foreignKey: {
destinationTableName: "users",
destinationTableColumnName: "id",
cascadeDelete: true,
},
},
{
fieldName: "title",
dataType: "TEXT",
notNullValue: true,
},
{
fieldName: "content",
dataType: "TEXT",
},
],
},
],
};
}
export { SQLiteSchemaManager }; export { SQLiteSchemaManager };

View File

@ -111,7 +111,7 @@ export type BUN_SQLITE_FieldSchemaType = {
fieldName?: string; fieldName?: string;
fieldDescription?: string; fieldDescription?: string;
originName?: string; originName?: string;
updatedField?: boolean; // updatedField?: boolean;
dataType: (typeof BUN_SQLITE_DATATYPES)[number]["value"]; dataType: (typeof BUN_SQLITE_DATATYPES)[number]["value"];
nullValue?: boolean; nullValue?: boolean;
notNullValue?: boolean; notNullValue?: boolean;