disable SQLite in Java & Swift (since they don't compile) & add SQL queueing on startup

At this point, the app compiles and runs in Android & iOS but DB operations fail.
This commit is contained in:
2025-06-03 19:59:28 -06:00
parent 9741b6a3e6
commit 705c6092a3
14 changed files with 283 additions and 123 deletions

View File

@@ -10,10 +10,9 @@ import {
SQLiteConnection,
SQLiteDBConnection,
CapacitorSQLite,
Changes,
} from "@capacitor-community/sqlite";
import { logger } from "../../utils/logger";
import { QueryExecResult, SqlValue } from "@/interfaces/database";
import { QueryExecResult } from "@/interfaces/database";
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
interface Migration {
@@ -21,6 +20,14 @@ interface Migration {
sql: string;
}
interface QueuedOperation {
type: "run" | "query" | "getOneRow" | "getAll";
sql: string;
params: unknown[];
resolve: (value: unknown) => void;
reject: (reason: unknown) => void;
}
/**
* Platform service implementation for Capacitor (mobile) platform.
* Provides native mobile functionality through Capacitor plugins for:
@@ -34,12 +41,40 @@ export class CapacitorPlatformService implements PlatformService {
private db: SQLiteDBConnection | null = null;
private dbName = "timesafari.db";
private initialized = false;
private initializationPromise: Promise<void> | null = null;
private operationQueue: Array<QueuedOperation> = [];
private isProcessingQueue: boolean = false;
constructor() {
this.sqlite = new SQLiteConnection(CapacitorSQLite);
}
private async initializeDatabase(): Promise<void> {
// If already initialized, return immediately
if (this.initialized) {
return;
}
// If initialization is in progress, wait for it
if (this.initializationPromise) {
return this.initializationPromise;
}
// Start initialization
this.initializationPromise = this._initialize();
try {
await this.initializationPromise;
} catch (error) {
logger.error(
"[CapacitorPlatformService] Initialize method failed:",
error,
);
this.initializationPromise = null; // Reset on failure
throw error;
}
}
private async _initialize(): Promise<void> {
if (this.initialized) {
return;
}
@@ -57,16 +92,142 @@ export class CapacitorPlatformService implements PlatformService {
await this.db.open();
// Set journal mode to WAL for better performance
await this.db.execute("PRAGMA journal_mode=WAL;");
// await this.db.execute("PRAGMA journal_mode=WAL;");
// Run migrations
await this.runMigrations();
this.initialized = true;
logger.log("SQLite database initialized successfully");
logger.log(
"[CapacitorPlatformService] SQLite database initialized successfully",
);
// Start processing the queue after initialization
this.processQueue();
} catch (error) {
logger.error("Error initializing SQLite database:", error);
throw new Error("Failed to initialize database");
logger.error(
"[CapacitorPlatformService] Error initializing SQLite database:",
error,
);
throw new Error(
"[CapacitorPlatformService] Failed to initialize database",
);
}
}
private async processQueue(): Promise<void> {
if (this.isProcessingQueue || !this.initialized || !this.db) {
return;
}
this.isProcessingQueue = true;
while (this.operationQueue.length > 0) {
const operation = this.operationQueue.shift();
if (!operation) continue;
try {
let result: unknown;
switch (operation.type) {
case "run": {
const runResult = await this.db.run(
operation.sql,
operation.params,
);
result = {
changes: runResult.changes?.changes || 0,
lastId: runResult.changes?.lastId,
};
break;
}
case "query": {
const queryResult = await this.db.query(
operation.sql,
operation.params,
);
result = {
columns: [], // SQLite plugin doesn't provide column names
values: queryResult.values || [],
};
break;
}
case "getOneRow": {
const oneRowResult = await this.db.query(
operation.sql,
operation.params,
);
result = oneRowResult.values?.[0];
break;
}
case "getAll": {
const allResult = await this.db.query(
operation.sql,
operation.params,
);
result = allResult.values || [];
break;
}
}
operation.resolve(result);
} catch (error) {
logger.error(
"[CapacitorPlatformService] Error while processing SQL queue:",
error,
" ... for sql:",
operation.sql,
" ... with params:",
operation.params,
);
operation.reject(error);
}
}
this.isProcessingQueue = false;
}
private async queueOperation<R>(
type: QueuedOperation["type"],
sql: string,
params: unknown[] = [],
): Promise<R> {
return new Promise<R>((resolve, reject) => {
const operation: QueuedOperation = {
type,
sql,
params,
resolve: (value: unknown) => resolve(value as R),
reject,
};
this.operationQueue.push(operation);
// If we're already initialized, start processing the queue
if (this.initialized && this.db) {
this.processQueue();
}
});
}
private async waitForInitialization(): Promise<void> {
// If we have an initialization promise, wait for it
if (this.initializationPromise) {
await this.initializationPromise;
return;
}
// If not initialized and no promise, start initialization
if (!this.initialized) {
await this.initializeDatabase();
return;
}
// If initialized but no db, something went wrong
if (!this.db) {
logger.error(
"[CapacitorPlatformService] Database not properly initialized after await waitForInitialization() - initialized flag is true but db is null",
);
throw new Error(
"[CapacitorPlatformService] The database could not be initialized. We recommend you restart or reinstall.",
);
}
}
@@ -166,7 +327,7 @@ export class CapacitorPlatformService implements PlatformService {
CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name);
CREATE TABLE IF NOT EXISTS logs (
date TEXT PRIMARY KEY,
date TEXT,
message TEXT NOT NULL
);
@@ -660,24 +821,8 @@ export class CapacitorPlatformService implements PlatformService {
* @see PlatformService.dbQuery
*/
async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
await this.initializeDatabase();
if (!this.db) {
throw new Error("Database not initialized");
}
try {
const result = await this.db.query(sql, params || []);
const values = result.values || [];
return {
columns: [], // SQLite plugin doesn't provide column names in query result
values: values as SqlValue[][],
};
} catch (error) {
logger.error("Error executing query:", error);
throw new Error(
`Database query failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
await this.waitForInitialization();
return this.queueOperation<QueryExecResult>("query", sql, params || []);
}
/**
@@ -687,23 +832,11 @@ export class CapacitorPlatformService implements PlatformService {
sql: string,
params?: unknown[],
): Promise<{ changes: number; lastId?: number }> {
await this.initializeDatabase();
if (!this.db) {
throw new Error("Database not initialized");
}
try {
const result = await this.db.run(sql, params || []);
const changes = result.changes as Changes;
return {
changes: changes?.changes || 0,
lastId: changes?.lastId,
};
} catch (error) {
logger.error("Error executing statement:", error);
throw new Error(
`Database execution failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
await this.waitForInitialization();
return this.queueOperation<{ changes: number; lastId?: number }>(
"run",
sql,
params || [],
);
}
}