diff --git a/src/nativeWorker.ts b/src/nativeWorker.ts index 031a813..786ee55 100644 --- a/src/nativeWorker.ts +++ b/src/nativeWorker.ts @@ -584,8 +584,9 @@ export async function createNativeDatabaseConnection( // 1. Add columns back for (const col of deletedColumns) { validateSqlType(col.type); // Validate type - // We can't batch DDL usually, so run immediately - await worker.call('run', [`ALTER TABLE ${escapeIdentifier(targetTable)} ADD COLUMN ${escapeIdentifier(col.name)} ${col.type}`]); + batch.push({ + sql: `ALTER TABLE ${escapeIdentifier(targetTable)} ADD COLUMN ${escapeIdentifier(col.name)} ${col.type}` + }); } // 2. Restore values for (const col of deletedColumns) { @@ -676,8 +677,12 @@ export async function createNativeDatabaseConnection( case 'column_drop': if (deletedColumns) { + const batch = []; for (const col of deletedColumns) { - await worker.call('run', [`ALTER TABLE ${escapeIdentifier(targetTable)} DROP COLUMN ${escapeIdentifier(col.name)}`]); + batch.push({ sql: `ALTER TABLE ${escapeIdentifier(targetTable)} DROP COLUMN ${escapeIdentifier(col.name)}` }); + } + if (batch.length > 0) { + await worker.call('execBatch', [batch]); } } break; diff --git a/tests/benchmarks/native_worker_ddl_batch.ts b/tests/benchmarks/native_worker_ddl_batch.ts new file mode 100644 index 0000000..03eb8cc --- /dev/null +++ b/tests/benchmarks/native_worker_ddl_batch.ts @@ -0,0 +1,52 @@ +import '../unit/vscode_mock_setup'; +import { createNativeDatabaseConnection } from '../../src/nativeWorker'; +import * as vscode from 'vscode'; +import * as path from 'path'; + +async function runBenchmark() { + const bundle = await createNativeDatabaseConnection(vscode.Uri.file(process.cwd())); + const loadResult = await bundle.loadDatabase({ buffer: new Uint8Array() }); + const db = loadResult.databaseOps; + + // create table + await db.createTable('test_table', [ + { name: 'id', type: 'INTEGER', primaryKey: true }, + ...Array.from({ length: 50 }, (_, i) => ({ name: `col_${i}`, type: 'TEXT' })) + ]); + + // insert row + await db.insertRow('test_table', { + id: 1, + ...Object.fromEntries(Array.from({ length: 50 }, (_, i) => [`col_${i}`, `val_${i}`])) + }); + + const mod = { + modificationType: 'column_drop' as const, + targetTable: 'test_table', + deletedColumns: Array.from({ length: 50 }, (_, i) => ({ + name: `col_${i}`, + type: 'TEXT', + data: [{ rowId: 1, value: `val_${i}` }] + })) + }; + + console.log('Warming up...'); + + // warmup + await db.undoModification(mod); + await db.redoModification(mod); + + console.log('Running benchmark...'); + + const start = performance.now(); + for (let i = 0; i < 5; i++) { + await db.undoModification(mod); + await db.redoModification(mod); + } + const end = performance.now(); + console.log(`Time taken: ${(end - start).toFixed(2)}ms`); + + bundle.workerMethods[Symbol.dispose](); +} + +runBenchmark().catch(console.error); diff --git a/tests/performance/native_undo_redo_benchmark.ts b/tests/performance/native_undo_redo_benchmark.ts new file mode 100644 index 0000000..aae7ff2 --- /dev/null +++ b/tests/performance/native_undo_redo_benchmark.ts @@ -0,0 +1,67 @@ +import '../unit/vscode_mock_setup'; // Must be first +import * as vsc from 'vscode'; +import { createNativeDatabaseConnection } from '../../src/nativeWorker'; +import { performance } from 'perf_hooks'; + +const mockReporter = { + sendTelemetryEvent: () => {}, + sendTelemetryErrorEvent: () => {}, + dispose: () => {} +} as any; + +async function runBenchmark() { + console.log('Starting Native Undo/Redo Deletion Benchmark...'); + + const extensionUri = vsc.Uri.file(process.cwd()); + + try { + const connectionBundle = await createNativeDatabaseConnection(extensionUri, mockReporter); + + const dbPath = ':memory:'; + const dbUri = vsc.Uri.file(dbPath); + + const { databaseOps } = await connectionBundle.establishConnection(dbUri, 'bench_db'); + + const numCols = 50; + const columns = Array.from({ length: numCols }, (_, i) => `col${i}`); + + console.log(`Creating table with ${numCols} columns...`); + const createTableSql = `CREATE TABLE test_table (id INTEGER PRIMARY KEY, ${columns.map(c => `${c} TEXT`).join(', ')})`; + await databaseOps.executeQuery(createTableSql); + + await databaseOps.insertRow('test_table', { id: 1, ...Object.fromEntries(columns.map(c => [c, 'val'])) }); + + const mod = { + modificationType: 'column_drop' as const, + targetTable: 'test_table', + deletedColumns: columns.map((col, i) => ({ + name: col, + type: 'TEXT', + data: [{ rowId: 1, value: 'val' }] + })) + }; + + // First do a drop + await databaseOps.deleteColumns('test_table', columns); + + console.log('Measuring undo/redo of column_drop...'); + const start = performance.now(); + + // 1. Undo (Adds columns back) + await databaseOps.undoModification(mod); + + // 2. Redo (Drops columns again) + await databaseOps.redoModification(mod); + + const end = performance.now(); + console.log(`Undo + Redo took ${(end - start).toFixed(2)}ms`); + + if (typeof connectionBundle.workerMethods[Symbol.dispose] === 'function') { + connectionBundle.workerMethods[Symbol.dispose](); + } + } catch (e) { + console.error('Benchmark failed:', e); + } +} + +runBenchmark();