Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: on iOS db locked error #827

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 65 additions & 65 deletions ios/Classes/DBManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -117,86 +117,86 @@ -(void)runQuery:(const char *)query isQueryExecutable:(BOOL)queryExecutable{
}
return;
}
if(openDatabaseResult == SQLITE_OK) {
if (debug) {
NSLog(@"open DB successfully");
}

// Declare a sqlite3_stmt object in which will be stored the query after having been compiled into a SQLite statement.
sqlite3_stmt *compiledStatement;

// Load all data from database to memory.
int prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, query, -1, &compiledStatement, NULL);
if(prepareStatementResult == SQLITE_OK) {
// Check if the query is non-executable.
if (!queryExecutable){
// In this case data must be loaded from the database.

if (debug) {
NSLog(@"open DB successfully");
}

// Declare a sqlite3_stmt object in which will be stored the query after having been compiled into a SQLite statement.
sqlite3_stmt *compiledStatement;

// Load all data from database to memory.
int prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, query, -1, &compiledStatement, NULL);
if(prepareStatementResult == SQLITE_OK) {
// Check if the query is non-executable.
if (!queryExecutable){
// In this case data must be loaded from the database.

// Declare an array to keep the data for each fetched row.
NSMutableArray *arrDataRow;

// Loop through the results and add them to the results array row by row.
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Initialize the mutable array that will contain the data of a fetched row.
arrDataRow = [[NSMutableArray alloc] init];

// Declare an array to keep the data for each fetched row.
NSMutableArray *arrDataRow;
// Get the total number of columns.
int totalColumns = sqlite3_column_count(compiledStatement);

// Loop through the results and add them to the results array row by row.
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Initialize the mutable array that will contain the data of a fetched row.
arrDataRow = [[NSMutableArray alloc] init];

// Get the total number of columns.
int totalColumns = sqlite3_column_count(compiledStatement);
// Go through all columns and fetch each column data.
for (int i=0; i<totalColumns; i++){
// Convert the column data to text (characters).
char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);

// Go through all columns and fetch each column data.
for (int i=0; i<totalColumns; i++){
// Convert the column data to text (characters).
char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);

// If there are contents in the currenct column (field) then add them to the current row array.
if (dbDataAsChars != NULL) {
// Convert the characters to string.
[arrDataRow addObject:[NSString stringWithUTF8String:dbDataAsChars]];
}

// Keep the current column name.
if (self.arrColumnNames.count != totalColumns) {
dbDataAsChars = (char *)sqlite3_column_name(compiledStatement, i);
[self.arrColumnNames addObject:[NSString stringWithUTF8String:dbDataAsChars]];
}
// If there are contents in the currenct column (field) then add them to the current row array.
if (dbDataAsChars != NULL) {
// Convert the characters to string.
[arrDataRow addObject:[NSString stringWithUTF8String:dbDataAsChars]];
}

// Store each fetched data row in the results array, but first check if there is actually data.
if (arrDataRow.count > 0) {
[self.arrResults addObject:arrDataRow];
// Keep the current column name.
if (self.arrColumnNames.count != totalColumns) {
dbDataAsChars = (char *)sqlite3_column_name(compiledStatement, i);
[self.arrColumnNames addObject:[NSString stringWithUTF8String:dbDataAsChars]];
}
}
}
else {
// This is the case of an executable query (insert, update, ...).

// Execute the query.
int executeQueryResults = sqlite3_step(compiledStatement);
if (executeQueryResults == SQLITE_DONE) {
// Keep the affected rows.
self.affectedRows = sqlite3_changes(sqlite3Database);

// Keep the last inserted row ID.
self.lastInsertedRowID = sqlite3_last_insert_rowid(sqlite3Database);
}
else {
// If could not execute the query show the error message on the debugger.
if (debug) {
NSLog(@"DB Error: %s", sqlite3_errmsg(sqlite3Database));
}
// Store each fetched data row in the results array, but first check if there is actually data.
if (arrDataRow.count > 0) {
[self.arrResults addObject:arrDataRow];
}
}
}
else {
// In the database cannot be opened then show the error message on the debugger.
if (debug) {
NSLog(@"%s", sqlite3_errmsg(sqlite3Database));
// This is the case of an executable query (insert, update, ...).

// Execute the query.
int executeQueryResults = sqlite3_step(compiledStatement);
if (executeQueryResults == SQLITE_DONE) {
// Keep the affected rows.
self.affectedRows = sqlite3_changes(sqlite3Database);

// Keep the last inserted row ID.
self.lastInsertedRowID = sqlite3_last_insert_rowid(sqlite3Database);
}
else {
// If could not execute the query show the error message on the debugger.
if (debug) {
NSLog(@"DB Error: %s", sqlite3_errmsg(sqlite3Database));
}
}
}

// Release the compiled statement from memory.
sqlite3_finalize(compiledStatement);
}
else {
// In the database cannot be opened then show the error message on the debugger.
if (debug) {
NSLog(@"%s", sqlite3_errmsg(sqlite3Database));
}
}

// Release the compiled statement from memory.
sqlite3_finalize(compiledStatement);


// Close the database.
sqlite3_close(sqlite3Database);
Expand Down
39 changes: 25 additions & 14 deletions ios/Classes/FlutterDownloaderPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ @interface FlutterDownloaderPlugin()<NSURLSessionTaskDelegate, NSURLSessionDownl
NSMutableArray *_eventQueue;
}

@property(nonatomic, strong) dispatch_queue_t databaseQueue;

/// The flag ensures that the database task avoids be marked as other status after be marked as canceled in the termination.
@property(nonatomic, assign, getter=isDatabaseQueueTerminated) BOOL databaseQueueTerminated;

@end

@implementation FlutterDownloaderPlugin
Expand All @@ -56,9 +51,8 @@ @implementation FlutterDownloaderPlugin
static int64_t _callbackHandle = 0;
static int _step = 10;
static NSMutableDictionary<NSString*, NSMutableDictionary*> *_runningTaskById = nil;


@synthesize databaseQueue;
static dispatch_queue_t databaseQueue;
static void *isOnDatabaseQueueKey;

- (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar;
{
Expand Down Expand Up @@ -92,7 +86,15 @@ - (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar;
if (debug) {
NSLog(@"database path: %@", dbPath);
}
databaseQueue = dispatch_queue_create("vn.hunghd.flutter_downloader", 0);

if (databaseQueue == nil) {
databaseQueue = dispatch_queue_create("vn.hunghd.flutter_downloader", 0);

isOnDatabaseQueueKey = &isOnDatabaseQueueKey;
void *nonNullUnusedPointer = (__bridge void *)(self.class);
dispatch_queue_set_specific(databaseQueue, isOnDatabaseQueueKey, nonNullUnusedPointer, NULL);
}

_dbManager = [[DBManager alloc] initWithDatabaseFilePath:dbPath];

if (_runningTaskById == nil) {
Expand Down Expand Up @@ -298,11 +300,13 @@ - (void)sendUpdateProgressForTaskId: (NSString*)taskId inStatus: (NSNumber*) sta
}

- (void)executeInDatabaseQueueForTask:(void (^)(void))task {
__typeof__(self) __weak weakSelf = self;
dispatch_sync(databaseQueue, ^{
if (weakSelf.isDatabaseQueueTerminated) return;
if (dispatch_get_specific(isOnDatabaseQueueKey)) {
if (task) task();
});
} else {
dispatch_sync(databaseQueue, ^{
if (task) task();
});
}
}

- (BOOL)openDocumentWithURL:(NSURL*)url {
Expand Down Expand Up @@ -539,7 +543,14 @@ - (NSDictionary*)loadTaskWithId:(NSString*)taskId
return [_runningTaskById objectForKey:taskId];
} else {
NSString *query = [NSString stringWithFormat:@"SELECT * FROM task WHERE task_id = \"%@\" ORDER BY id DESC LIMIT 1", taskId];
NSArray *records = [[NSArray alloc] initWithArray:[_dbManager loadDataFromDB:query]];

__block NSArray *records;

__typeof__(_dbManager) __weak weakDbManager = _dbManager;
[self executeInDatabaseQueueForTask:^{
records = [[NSArray alloc] initWithArray:[weakDbManager loadDataFromDB:query]];
}];

if (debug) {
NSLog(@"Load task successfully");
}
Expand Down