Skip to content

Commit dff35fe

Browse files
author
Lasim
committed
feat(backend): enhance SQL statement handling for Turso compatibility
1 parent cf20273 commit dff35fe

File tree

1 file changed

+76
-43
lines changed

1 file changed

+76
-43
lines changed

services/backend/src/db/index.ts

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,33 @@ function generateSchema(): AnySchema {
6666
return generatedSchema;
6767
}
6868

69+
/**
70+
* Split SQL content into individual statements for Turso compatibility
71+
*/
72+
function splitSQLStatements(sqlContent: string): string[] {
73+
// First split by the statement breakpoint marker
74+
const sections = sqlContent.split('--> statement-breakpoint');
75+
const statements: string[] = [];
76+
77+
for (const section of sections) {
78+
const trimmed = section.trim();
79+
if (!trimmed) continue;
80+
81+
// Further split by semicolons to ensure each statement is separate
82+
// This handles cases where multiple statements aren't separated by breakpoints
83+
const subStatements = trimmed.split(';');
84+
85+
for (const subStatement of subStatements) {
86+
const cleanStatement = subStatement.trim();
87+
if (cleanStatement) {
88+
// Add semicolon back if it was removed by split
89+
statements.push(cleanStatement + ';');
90+
}
91+
}
92+
}
93+
94+
return statements;
95+
}
6996

7097
/**
7198
* Create database instance based on configuration
@@ -200,18 +227,8 @@ async function applyMigrations(db: AnyDatabase, config: DatabaseConfig, logger:
200227
// SQLite uses the better-sqlite3 client
201228
(db as any).$client.exec(createTableQuery);
202229
} else if (config.type === 'turso') {
203-
// Turso uses libSQL client - try different approaches
204-
if ((db as any).$client && typeof (db as any).$client.execute === 'function') {
205-
// Use execute method for libSQL
206-
await (db as any).$client.execute(createTableQuery);
207-
} else if ((db as any).$client && typeof (db as any).$client.prepare === 'function') {
208-
// Use prepare/run for libSQL
209-
const prepared = (db as any).$client.prepare(createTableQuery);
210-
await prepared.run();
211-
} else {
212-
// Fallback: use Drizzle's run method
213-
await db.run(createTableQuery);
214-
}
230+
// Turso uses libSQL client - execute single statement
231+
await (db as any).$client.execute(createTableQuery.trim());
215232
}
216233

217234
if (!isTestMode()) {
@@ -240,18 +257,8 @@ async function applyMigrations(db: AnyDatabase, config: DatabaseConfig, logger:
240257
appliedMigrations = (db as any).$client.prepare(selectAppliedQuery).all();
241258
} else if (config.type === 'turso') {
242259
// Turso uses libSQL client
243-
if ((db as any).$client && typeof (db as any).$client.execute === 'function') {
244-
const result = await (db as any).$client.execute(selectAppliedQuery);
245-
appliedMigrations = result.rows || [];
246-
} else if ((db as any).$client && typeof (db as any).$client.prepare === 'function') {
247-
const prepared = (db as any).$client.prepare(selectAppliedQuery);
248-
const result = await prepared.all();
249-
appliedMigrations = result || [];
250-
} else {
251-
// Fallback: use Drizzle's all method
252-
const result = await db.all(selectAppliedQuery);
253-
appliedMigrations = result || [];
254-
}
260+
const result = await (db as any).$client.execute(selectAppliedQuery);
261+
appliedMigrations = result.rows || [];
255262
}
256263

257264
if (!isTestMode()) {
@@ -294,44 +301,70 @@ async function applyMigrations(db: AnyDatabase, config: DatabaseConfig, logger:
294301
}
295302
const migrationFilePath = path.join(migrationsPath, file);
296303
const sqlContent = await fs.readFile(migrationFilePath, 'utf8');
297-
const statements = sqlContent.split('--> statement-breakpoint');
304+
305+
// Use improved statement splitting for Turso
306+
const statements = config.type === 'turso'
307+
? splitSQLStatements(sqlContent)
308+
: sqlContent.split('--> statement-breakpoint');
298309

299310
try {
311+
let statementCount = 0;
300312
for (const statement of statements) {
301313
const trimmedStatement = statement.trim();
302314
if (trimmedStatement) {
315+
statementCount++;
303316
if (config.type === 'sqlite') {
304317
(db as any).$client.exec(trimmedStatement);
305318
} else if (config.type === 'turso') {
306-
// Turso uses libSQL client
307-
if ((db as any).$client && typeof (db as any).$client.execute === 'function') {
319+
// Log each statement for debugging
320+
if (!isTestMode()) {
321+
logger.debug({
322+
operation: 'apply_migrations',
323+
migrationFile: file,
324+
statementNumber: statementCount,
325+
statementPreview: trimmedStatement.substring(0, 100) + (trimmedStatement.length > 100 ? '...' : ''),
326+
databaseType: config.type
327+
}, `Executing statement ${statementCount}`);
328+
}
329+
330+
// Turso - execute each statement individually
331+
try {
308332
await (db as any).$client.execute(trimmedStatement);
309-
} else if ((db as any).$client && typeof (db as any).$client.prepare === 'function') {
310-
const prepared = (db as any).$client.prepare(trimmedStatement);
311-
await prepared.run();
312-
} else {
313-
// Fallback: use Drizzle's run method
314-
await db.run(trimmedStatement);
333+
} catch (stmtError: any) {
334+
logger.error({
335+
operation: 'apply_migrations',
336+
migrationFile: file,
337+
statementNumber: statementCount,
338+
statement: trimmedStatement,
339+
databaseType: config.type,
340+
error: stmtError,
341+
errorMessage: stmtError.message
342+
}, `Failed to execute statement ${statementCount}`);
343+
throw stmtError;
315344
}
316345
}
317346
}
318347
}
319348

349+
if (!isTestMode()) {
350+
logger.info({
351+
operation: 'apply_migrations',
352+
migrationFile: file,
353+
statementCount,
354+
databaseType: config.type
355+
}, `Successfully executed ${statementCount} statements from migration`);
356+
}
357+
320358
// Record the migration as applied
321359
const insertMigrationQuery = `INSERT INTO ${MIGRATIONS_TABLE_NAME} (migration_name) VALUES (?)`;
322360
if (config.type === 'sqlite') {
323361
(db as any).$client.prepare(insertMigrationQuery).run(file);
324362
} else if (config.type === 'turso') {
325-
// Turso uses libSQL client
326-
if ((db as any).$client && typeof (db as any).$client.execute === 'function') {
327-
await (db as any).$client.execute(insertMigrationQuery, [file]);
328-
} else if ((db as any).$client && typeof (db as any).$client.prepare === 'function') {
329-
const prepared = (db as any).$client.prepare(insertMigrationQuery);
330-
await prepared.run([file]);
331-
} else {
332-
// Fallback: use Drizzle's run method
333-
await db.run(insertMigrationQuery, [file]);
334-
}
363+
// Turso uses libSQL client with parameterized query
364+
await (db as any).$client.execute({
365+
sql: insertMigrationQuery,
366+
args: [file]
367+
});
335368
}
336369

337370
if (!isTestMode()) {

0 commit comments

Comments
 (0)