FLEXSQLiteDatabaseManager.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 <sqlite3.h>
  12. static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
  13. @interface FLEXSQLiteDatabaseManager ()
  14. @property (nonatomic, readonly) sqlite3 *db;
  15. @property (nonatomic, copy) NSString *path;
  16. @end
  17. @implementation FLEXSQLiteDatabaseManager
  18. #pragma mark - FLEXDatabaseManager
  19. + (instancetype)managerForDatabase:(NSString *)path {
  20. return [[self alloc] initWithPath:path];
  21. }
  22. - (instancetype)initWithPath:(NSString *)path {
  23. self = [super init];
  24. if (self) {
  25. self.path = path;;
  26. }
  27. return self;
  28. }
  29. - (BOOL)open {
  30. if (_db) {
  31. return YES;
  32. }
  33. int err = sqlite3_open(self.path.UTF8String, &_db);
  34. #if SQLITE_HAS_CODEC
  35. NSString *defaultSqliteDatabasePassword = FLEXManager.sharedManager.defaultSqliteDatabasePassword;
  36. if (defaultSqliteDatabasePassword) {
  37. const char *key = defaultSqliteDatabasePassword.UTF8String;
  38. sqlite3_key(_db, key, (int)strlen(key));
  39. }
  40. #endif
  41. if (err != SQLITE_OK) {
  42. NSLog(@"error opening!: %d", err);
  43. return NO;
  44. }
  45. return YES;
  46. }
  47. - (BOOL)close {
  48. if (!_db) {
  49. return YES;
  50. }
  51. int rc;
  52. BOOL retry, triedFinalizingOpenStatements = NO;
  53. do {
  54. retry = NO;
  55. rc = sqlite3_close(_db);
  56. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  57. if (!triedFinalizingOpenStatements) {
  58. triedFinalizingOpenStatements = YES;
  59. sqlite3_stmt *pStmt;
  60. while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) {
  61. NSLog(@"Closing leaked statement");
  62. sqlite3_finalize(pStmt);
  63. retry = YES;
  64. }
  65. }
  66. } else if (SQLITE_OK != rc) {
  67. NSLog(@"error closing!: %d", rc);
  68. }
  69. } while (retry);
  70. _db = nil;
  71. return YES;
  72. }
  73. - (NSArray<NSString *> *)queryAllTables {
  74. return [[self executeQuery:QUERY_TABLENAMES_SQL] flex_mapped:^id(NSArray *table, NSUInteger idx) {
  75. return table.firstObject;
  76. }];
  77. }
  78. - (NSArray<NSString *> *)queryAllColumnsWithTableName:(NSString *)tableName {
  79. NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName];
  80. NSArray<NSDictionary *> *results = [self executeQueryWithColumns:sql];
  81. return [results flex_mapped:^id(NSDictionary *column, NSUInteger idx) {
  82. return column[@"name"];
  83. }];
  84. }
  85. - (NSArray<NSArray *> *)queryAllDataWithTableName:(NSString *)tableName {
  86. return [self executeQuery:[@"SELECT * FROM "
  87. stringByAppendingString:tableName
  88. ]];
  89. }
  90. #pragma mark - Private
  91. /// @return an array of rows, where each row is an array
  92. /// containing the values of each column for that row
  93. - (NSArray<NSArray *> *)executeQuery:(NSString *)sql {
  94. [self open];
  95. NSMutableArray<NSArray *> *results = [NSMutableArray array];
  96. sqlite3_stmt *pstmt;
  97. if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0) == SQLITE_OK) {
  98. while (sqlite3_step(pstmt) == SQLITE_ROW) {
  99. int num_cols = sqlite3_data_count(pstmt);
  100. if (num_cols > 0) {
  101. int columnCount = sqlite3_column_count(pstmt);
  102. [results addObject:[NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) {
  103. return [self objectForColumnIndex:(int)i stmt:pstmt];
  104. }]];
  105. }
  106. }
  107. }
  108. [self close];
  109. return results;
  110. }
  111. /// Like \c executeQuery: except that a list of dictionaries are returned,
  112. /// where the keys are column names and the values are the data.
  113. - (NSArray<NSDictionary *> *)executeQueryWithColumns:(NSString *)sql {
  114. [self open];
  115. NSMutableArray<NSDictionary *> *results = [NSMutableArray array];
  116. sqlite3_stmt *pstmt;
  117. if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0) == SQLITE_OK) {
  118. while (sqlite3_step(pstmt) == SQLITE_ROW) {
  119. int num_cols = sqlite3_data_count(pstmt);
  120. if (num_cols > 0) {
  121. int columnCount = sqlite3_column_count(pstmt);
  122. NSMutableDictionary *rowFields = [NSMutableDictionary new];
  123. for (int i = 0; i < columnCount; i++) {
  124. id value = [self objectForColumnIndex:(int)i stmt:pstmt];
  125. rowFields[@(sqlite3_column_name(pstmt, i))] = value;
  126. }
  127. [results addObject:rowFields];
  128. }
  129. }
  130. }
  131. [self close];
  132. return results;
  133. }
  134. - (id)objectForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt*)stmt {
  135. int columnType = sqlite3_column_type(stmt, columnIdx);
  136. switch (columnType) {
  137. case SQLITE_INTEGER:
  138. return @(sqlite3_column_int64(stmt, columnIdx)).stringValue;
  139. case SQLITE_FLOAT:
  140. return @(sqlite3_column_double(stmt, columnIdx)).stringValue;
  141. case SQLITE_BLOB:
  142. return [NSString stringWithFormat:@"Data (%@ bytes)",
  143. @([self dataForColumnIndex:columnIdx stmt:stmt].length)
  144. ];
  145. default:
  146. // Default to a string for everything else
  147. return [self stringForColumnIndex:columnIdx stmt:stmt] ?: NSNull.null;
  148. }
  149. }
  150. - (NSString *)stringForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt {
  151. if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || columnIdx < 0) {
  152. return nil;
  153. }
  154. const char *text = (const char *)sqlite3_column_text(stmt, columnIdx);
  155. return text ? @(text) : nil;
  156. }
  157. - (NSData *)dataForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt {
  158. if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
  159. return nil;
  160. }
  161. const void *blob = sqlite3_column_blob(stmt, columnIdx);
  162. NSInteger size = (NSInteger)sqlite3_column_bytes(stmt, columnIdx);
  163. return blob ? [NSData dataWithBytes:blob length:size] : nil;
  164. }
  165. @end