/**
* Transaction Tools for Native SQLite Adapter
*
* Provides transaction control tools for complex multi-statement operations.
*/
import { z } from 'zod';
import type { ToolDefinition, RequestContext } from '../../../types/index.js';
import type { NativeSqliteAdapter } from '../NativeSqliteAdapter.js';
// Schemas
const BeginTransactionSchema = z.object({
mode: z.enum(['deferred', 'immediate', 'exclusive']).optional().default('deferred')
.describe('Transaction mode: deferred waits for first write, immediate acquires lock immediately, exclusive blocks all access')
});
const SavepointSchema = z.object({
name: z.string().describe('Savepoint name')
});
const ExecuteInTransactionSchema = z.object({
statements: z.array(z.string()).describe('Array of SQL statements to execute in order'),
rollbackOnError: z.boolean().optional().default(true)
.describe('If true, rollback all changes when any statement fails')
});
/**
* Get all transaction tools
*/
export function getTransactionTools(adapter: NativeSqliteAdapter): ToolDefinition[] {
return [
createBeginTransactionTool(adapter),
createCommitTransactionTool(adapter),
createRollbackTransactionTool(adapter),
createSavepointTool(adapter),
createReleaseSavepointTool(adapter),
createRollbackToSavepointTool(adapter),
createExecuteInTransactionTool(adapter)
];
}
/**
* Begin transaction
*/
function createBeginTransactionTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_begin',
description: 'Begin a new transaction. Use immediate or exclusive mode for write-heavy operations.',
group: 'admin',
inputSchema: BeginTransactionSchema,
requiredScopes: ['write'],
handler: async (params: unknown, _context: RequestContext) => {
const input = BeginTransactionSchema.parse(params);
const mode = input.mode.toUpperCase();
await adapter.executeWriteQuery(`BEGIN ${mode} TRANSACTION`);
return {
success: true,
message: `Transaction started (${input.mode} mode)`,
mode: input.mode
};
}
};
}
/**
* Commit transaction
*/
function createCommitTransactionTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_commit',
description: 'Commit the current transaction, making all changes permanent.',
group: 'admin',
inputSchema: z.object({}),
requiredScopes: ['write'],
handler: (_params: unknown, _context: RequestContext) => {
adapter.commitTransaction();
return Promise.resolve({
success: true,
message: 'Transaction committed'
});
}
};
}
/**
* Rollback transaction
*/
function createRollbackTransactionTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_rollback',
description: 'Rollback the current transaction, discarding all changes.',
group: 'admin',
inputSchema: z.object({}),
requiredScopes: ['write'],
handler: (_params: unknown, _context: RequestContext) => {
adapter.rollbackTransaction();
return Promise.resolve({
success: true,
message: 'Transaction rolled back'
});
}
};
}
/**
* Create savepoint
*/
function createSavepointTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_savepoint',
description: 'Create a savepoint within the current transaction for partial rollback.',
group: 'admin',
inputSchema: SavepointSchema,
requiredScopes: ['write'],
handler: (params: unknown, _context: RequestContext) => {
const input = SavepointSchema.parse(params);
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(input.name)) {
throw new Error('Invalid savepoint name');
}
adapter.savepoint(input.name);
return Promise.resolve({
success: true,
message: `Savepoint '${input.name}' created`,
savepoint: input.name
});
}
};
}
/**
* Release savepoint
*/
function createReleaseSavepointTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_release',
description: 'Release a savepoint, keeping the changes made since it was created.',
group: 'admin',
inputSchema: SavepointSchema,
requiredScopes: ['write'],
handler: (params: unknown, _context: RequestContext) => {
const input = SavepointSchema.parse(params);
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(input.name)) {
throw new Error('Invalid savepoint name');
}
adapter.releaseSavepoint(input.name);
return Promise.resolve({
success: true,
message: `Savepoint '${input.name}' released`,
savepoint: input.name
});
}
};
}
/**
* Rollback to savepoint
*/
function createRollbackToSavepointTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_rollback_to',
description: 'Rollback to a savepoint, discarding changes made after it was created.',
group: 'admin',
inputSchema: SavepointSchema,
requiredScopes: ['write'],
handler: (params: unknown, _context: RequestContext) => {
const input = SavepointSchema.parse(params);
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(input.name)) {
throw new Error('Invalid savepoint name');
}
adapter.rollbackToSavepoint(input.name);
return Promise.resolve({
success: true,
message: `Rolled back to savepoint '${input.name}'`,
savepoint: input.name
});
}
};
}
/**
* Execute multiple statements in a transaction
*/
function createExecuteInTransactionTool(adapter: NativeSqliteAdapter): ToolDefinition {
return {
name: 'sqlite_transaction_execute',
description: 'Execute multiple SQL statements in a single transaction. Automatically commits on success or rolls back on error.',
group: 'admin',
inputSchema: ExecuteInTransactionSchema,
requiredScopes: ['write'],
handler: async (params: unknown, _context: RequestContext) => {
const input = ExecuteInTransactionSchema.parse(params);
const results: { statement: string; rowsAffected?: number; error?: string }[] = [];
let success = true;
try {
adapter.beginTransaction();
for (const statement of input.statements) {
try {
const result = await adapter.executeWriteQuery(statement);
results.push({
statement: statement.substring(0, 100) + (statement.length > 100 ? '...' : ''),
rowsAffected: result.rowsAffected ?? 0
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
results.push({
statement: statement.substring(0, 100) + (statement.length > 100 ? '...' : ''),
error: message
});
if (input.rollbackOnError) {
throw error;
}
success = false;
}
}
adapter.commitTransaction();
return {
success,
message: success ? 'Transaction completed successfully' : 'Transaction completed with errors',
statementsExecuted: input.statements.length,
results
};
} catch (error) {
adapter.rollbackTransaction();
const message = error instanceof Error ? error.message : String(error);
return {
success: false,
message: `Transaction rolled back: ${message}`,
statementsExecuted: results.length,
results
};
}
}
};
}