From 855c034a35ea7f9957e02f848a9dd6e93b356428 Mon Sep 17 00:00:00 2001 From: DocNR Date: Tue, 4 Feb 2025 17:50:40 -0500 Subject: [PATCH] update to sqlite database WIP --- types/sqlite.ts | 109 ++++++---------------------- utils/db/db-service.ts | 160 +++++++++++++++++++---------------------- utils/db/schema.ts | 17 +++-- 3 files changed, 108 insertions(+), 178 deletions(-) diff --git a/types/sqlite.ts b/types/sqlite.ts index 2202658..91ec70b 100644 --- a/types/sqlite.ts +++ b/types/sqlite.ts @@ -1,89 +1,24 @@ // types/sqlite.ts -// Database interfaces -export interface SQLite { - rows: { - _array: any[]; - length: number; - item: (idx: number) => any; - }; - rowsAffected: number; - insertId?: number; - } - - // Transaction interfaces - export interface SQLiteCallback { - (transaction: SQLTransaction, resultSet: SQLite): void; - } - - export interface SQLErrorCallback { - (transaction: SQLTransaction, error: Error): boolean; - } - - export interface SQLTransaction { - executeSql: ( - sqlStatement: string, - args?: (string | number | null)[], - callback?: SQLiteCallback, - errorCallback?: SQLErrorCallback - ) => void; - } - - // Database error interfaces - export interface SQLError extends Error { - code?: number; - } - - // Database open options - export interface SQLiteOpenOptions { - enableChangeListener?: boolean; - useNewConnection?: boolean; - } - - // Result interfaces - export interface SQLiteRunResult { - insertId: number; - rowsAffected: number; - } - - export interface SQLiteRow { - [key: string]: any; - } - - export interface SQLiteResultSet { - insertId?: number; - rowsAffected: number; - rows: { - length: number; - _array: SQLiteRow[]; - item: (index: number) => SQLiteRow; - }; - } - - // Transaction callbacks - export interface TransactionCallback { - (tx: SQLTransaction): void; - } - - export interface TransactionErrorCallback { - (error: SQLError): void; - } - - export interface TransactionSuccessCallback { - (): void; - } - - // Database static type - export interface Database { - transaction( - callback: TransactionCallback, - error?: TransactionErrorCallback, - success?: TransactionSuccessCallback - ): void; - readTransaction( - callback: TransactionCallback, - error?: TransactionErrorCallback, - success?: TransactionSuccessCallback - ): void; - closeAsync(): Promise; - } \ No newline at end of file +export interface SQLiteRow { + [key: string]: any; +} + +export interface SQLiteResult { + rows: { + _array: T[]; + length: number; + item: (idx: number) => T; + }; + rowsAffected: number; + insertId?: number; +} + +export interface SQLiteError extends Error { + code?: number; +} + +export interface SQLiteStatement { + executeSync(params?: any[]): T[] | null; + finalizeSync(): void; +} \ No newline at end of file diff --git a/utils/db/db-service.ts b/utils/db/db-service.ts index ae5dd20..c6de429 100644 --- a/utils/db/db-service.ts +++ b/utils/db/db-service.ts @@ -1,119 +1,105 @@ // utils/db/db-service.ts -import * as SQLite from 'expo-sqlite'; import { - SQLite as SQLiteResult, - SQLTransaction, - SQLiteCallback, - SQLErrorCallback, - SQLError, - TransactionCallback, - TransactionErrorCallback, - TransactionSuccessCallback + openDatabaseSync, + SQLiteDatabase +} from 'expo-sqlite'; +import { + SQLiteResult, + SQLiteError, + SQLiteRow } from '@/types/sqlite'; export class DbService { - private db: SQLite.SQLiteDatabase; + private db: SQLiteDatabase | null = null; constructor(dbName: string) { - this.db = SQLite.openDatabaseSync(dbName); + try { + this.db = openDatabaseSync(dbName); + console.log('Database opened:', this.db); + } catch (error) { + console.error('Error opening database:', error); + throw error; + } } - async executeSql(sql: string, params: (string | number | null)[] = []): Promise { - return new Promise((resolve, reject) => { - this.db.withTransactionAsync(async (tx) => { - tx.executeSql( - sql, - params, - (_, result) => resolve(result), - (_, error) => { - console.error('SQL Error:', error); - reject(error); - return false; - } - ); - }).catch(error => { - console.error('Transaction Error:', error); - reject(error); - }); - }); + async executeSql( + sql: string, + params: (string | number | null)[] = [] + ): Promise> { + if (!this.db) { + throw new Error('Database not initialized'); + } + + try { + const statement = this.db.prepareSync(sql); + const result = statement.executeSync(params); + statement.finalizeSync(); + + return { + rows: { + _array: Array.isArray(result) ? result : [], + length: Array.isArray(result) ? result.length : 0, + item: (idx: number) => (Array.isArray(result) ? result[idx] : null) as T + }, + rowsAffected: Array.isArray(result) ? result.length : 0, + insertId: undefined // SQLite doesn't provide this directly + }; + } catch (error) { + console.error('SQL Error:', error, sql, params); + throw error; + } } - async executeWrite(sql: string, params: (string | number | null)[] = []): Promise { - return this.executeSql(sql, params); + async executeWrite( + sql: string, + params: (string | number | null)[] = [] + ): Promise> { + return this.executeSql(sql, params); } - async executeWriteMany(queries: { sql: string; args?: (string | number | null)[] }[]): Promise { - return new Promise((resolve, reject) => { - const results: SQLiteResult[] = []; - - this.db.withTransactionAsync(async (tx) => { - try { - for (const query of queries) { - await new Promise((resolveQuery, rejectQuery) => { - tx.executeSql( - query.sql, - query.args || [], - (_, result) => { - results.push(result); - resolveQuery(); - }, - (_, error) => { - console.error('SQL Error:', error); - rejectQuery(error); - return false; - } - ); - }); - } - resolve(results); - } catch (error) { - console.error('Transaction Error:', error); - reject(error); - } - }).catch(error => { - console.error('Transaction Error:', error); - reject(error); - }); - }); - } + async executeWriteMany( + queries: Array<{ + sql: string; + args?: (string | number | null)[] + }> + ): Promise[]> { + if (!this.db) { + throw new Error('Database not initialized'); + } - async withTransaction( - callback: (tx: SQLTransaction) => Promise - ): Promise { - return new Promise((resolve, reject) => { - this.db.withTransactionAsync(async (tx) => { - try { - const result = await callback(tx); - resolve(result); - } catch (error) { - console.error('Transaction Error:', error); - reject(error); - } - }).catch(error => { - console.error('Transaction Error:', error); - reject(error); - }); - }); + const results: SQLiteResult[] = []; + + for (const query of queries) { + try { + const result = await this.executeSql(query.sql, query.args || []); + results.push(result); + } catch (error) { + console.error('Error executing query:', query, error); + throw error; + } + } + + return results; } async tableExists(tableName: string): Promise { try { - const result = await this.executeSql( + const result = await this.executeSql<{ name: string }>( `SELECT name FROM sqlite_master WHERE type='table' AND name=?`, [tableName] ); - return result.rows.length > 0; + return result.rows._array.length > 0; } catch (error) { console.error('Error checking table existence:', error); return false; } } - async close(): Promise { + async initialize(): Promise { try { - await this.db.closeAsync(); + await this.executeSql('PRAGMA foreign_keys = ON;'); } catch (error) { - console.error('Error closing database:', error); + console.error('Error initializing database:', error); throw error; } } diff --git a/utils/db/schema.ts b/utils/db/schema.ts index 4ce5356..6209b2f 100644 --- a/utils/db/schema.ts +++ b/utils/db/schema.ts @@ -203,10 +203,19 @@ class Schema { } private async setVersion(version: number): Promise { - await this.db.executeSql( - 'INSERT INTO schema_version (version, updated_at) VALUES (?, ?)', - [version, Date.now()] - ); + try { + await this.db.executeSql( + 'DELETE FROM schema_version WHERE version = ?', + [version] + ); + await this.db.executeSql( + 'INSERT INTO schema_version (version, updated_at) VALUES (?, ?)', + [version, Date.now()] + ); + } catch (error) { + console.error('Error setting schema version:', error); + throw error; + } } }