FLEXSQLiteDatabaseManager.m 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //
  2. // PTDatabaseManager.m
  3. // PTDatabaseReader
  4. //
  5. // Created by Peng Tao on 15/11/23.
  6. // Copyright © 2015年 Peng Tao. All rights reserved.
  7. //
  8. #import "FLEXSQLiteDatabaseManager.h"
  9. #import "FLEXManager.h"
  10. #import "NSArray+Functional.h"
  11. #import "FLEXSQLResult.h"
  12. #import <sqlite3.h>
  13. static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
  14. @interface FLEXSQLiteDatabaseManager ()
  15. @property (nonatomic) sqlite3 *db;
  16. @property (nonatomic, copy) NSString *path;
  17. @end
  18. @implementation FLEXSQLiteDatabaseManager
  19. #pragma mark - FLEXDatabaseManager
  20. + (instancetype)managerForDatabase:(NSString *)path {
  21. return [[self alloc] initWithPath:path];
  22. }
  23. - (instancetype)initWithPath:(NSString *)path {
  24. self = [super init];
  25. if (self) {
  26. self.path = path;;
  27. }
  28. return self;
  29. }
  30. - (BOOL)open {
  31. if (self.db) {
  32. return YES;
  33. }
  34. int err = sqlite3_open(self.path.UTF8String, &_db);
  35. #if SQLITE_HAS_CODEC
  36. NSString *defaultSqliteDatabasePassword = FLEXManager.sharedManager.defaultSqliteDatabasePassword;
  37. if (defaultSqliteDatabasePassword) {
  38. const char *key = defaultSqliteDatabasePassword.UTF8String;
  39. sqlite3_key(_db, key, (int)strlen(key));
  40. }
  41. #endif
  42. if (err != SQLITE_OK) {
  43. NSLog(@"error opening!: %d", err);
  44. return NO;
  45. }
  46. return YES;
  47. }
  48. - (BOOL)close {
  49. if (!self.db) {
  50. return YES;
  51. }
  52. int rc;
  53. BOOL retry, triedFinalizingOpenStatements = NO;
  54. do {
  55. retry = NO;
  56. rc = sqlite3_close(_db);
  57. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  58. if (!triedFinalizingOpenStatements) {
  59. triedFinalizingOpenStatements = YES;
  60. sqlite3_stmt *pStmt;
  61. while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) {
  62. NSLog(@"Closing leaked statement");
  63. sqlite3_finalize(pStmt);
  64. retry = YES;
  65. }
  66. }
  67. } else if (SQLITE_OK != rc) {
  68. NSLog(@"error closing!: %d", rc);
  69. }
  70. } while (retry);
  71. self.db = nil;
  72. return YES;
  73. }
  74. - (NSArray<NSString *> *)queryAllTables {
  75. return [[self executeStatement:QUERY_TABLENAMES].rows flex_mapped:^id(NSArray *table, NSUInteger idx) {
  76. return table.firstObject;
  77. }];
  78. }
  79. - (NSArray<NSString *> *)queryAllColumnsOfTable:(NSString *)tableName {
  80. NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName];
  81. FLEXSQLResult *results = [self executeStatement:sql];
  82. return [results.keyedRows flex_mapped:^id(NSDictionary *column, NSUInteger idx) {
  83. return column[@"name"];
  84. }];
  85. }
  86. - (NSArray<NSArray *> *)queryAllDataInTable:(NSString *)tableName {
  87. return [self executeStatement:[@"SELECT * FROM "
  88. stringByAppendingString:tableName
  89. ]].rows;
  90. }
  91. - (FLEXSQLResult *)executeStatement:(NSString *)sql {
  92. [self open];
  93. FLEXSQLResult *result = nil;
  94. sqlite3_stmt *pstmt;
  95. if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0) == SQLITE_OK) {
  96. NSMutableArray<NSArray *> *rows = [NSMutableArray new];
  97. // Grab columns
  98. int columnCount = sqlite3_column_count(pstmt);
  99. NSArray<NSString *> *columns = [NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) {
  100. return @(sqlite3_column_name(pstmt, (int)i));
  101. }];
  102. // Execute statement
  103. int status;
  104. while ((status = sqlite3_step(pstmt)) == SQLITE_ROW) {
  105. // Grab rows if this is a selection query
  106. int dataCount = sqlite3_data_count(pstmt);
  107. if (dataCount > 0) {
  108. [rows addObject:[NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) {
  109. return [self objectForColumnIndex:(int)i stmt:pstmt];
  110. }]];
  111. }
  112. }
  113. if (status == SQLITE_DONE) {
  114. if (rows.count) {
  115. // We selected some rows
  116. result = [FLEXSQLResult columns:columns rows:rows];
  117. } else {
  118. // We executed a query like INSERT, UDPATE, or DELETE
  119. int rowsAffected = sqlite3_changes(_db);
  120. NSString *message = [NSString stringWithFormat:@"%d row(s) affected", rowsAffected];
  121. result = [FLEXSQLResult message:message];
  122. }
  123. } else {
  124. // An error occured executing the query
  125. result = [FLEXSQLResult message:@(sqlite3_errmsg(_db) ?: "(Execution: empty error)")];
  126. }
  127. } else {
  128. // An error occurred creating the prepared statement
  129. result = [FLEXSQLResult message:@(sqlite3_errmsg(_db) ?: "(Prepared statement: empty error)")];
  130. }
  131. sqlite3_finalize(pstmt);
  132. [self close];
  133. return result;
  134. }
  135. #pragma mark - Private
  136. - (id)objectForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt*)stmt {
  137. int columnType = sqlite3_column_type(stmt, columnIdx);
  138. switch (columnType) {
  139. case SQLITE_INTEGER:
  140. return @(sqlite3_column_int64(stmt, columnIdx)).stringValue;
  141. case SQLITE_FLOAT:
  142. return @(sqlite3_column_double(stmt, columnIdx)).stringValue;
  143. case SQLITE_BLOB:
  144. return [NSString stringWithFormat:@"Data (%@ bytes)",
  145. @([self dataForColumnIndex:columnIdx stmt:stmt].length)
  146. ];
  147. default:
  148. // Default to a string for everything else
  149. return [self stringForColumnIndex:columnIdx stmt:stmt] ?: NSNull.null;
  150. }
  151. }
  152. - (NSString *)stringForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt {
  153. if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || columnIdx < 0) {
  154. return nil;
  155. }
  156. const char *text = (const char *)sqlite3_column_text(stmt, columnIdx);
  157. return text ? @(text) : nil;
  158. }
  159. - (NSData *)dataForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt {
  160. if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
  161. return nil;
  162. }
  163. const void *blob = sqlite3_column_blob(stmt, columnIdx);
  164. NSInteger size = (NSInteger)sqlite3_column_bytes(stmt, columnIdx);
  165. return blob ? [NSData dataWithBytes:blob length:size] : nil;
  166. }
  167. @end