RCNConfigDBManager.m 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <sqlite3.h>
  17. #import "FirebaseRemoteConfig/Sources/RCNConfigDBManager.h"
  18. #import "FirebaseRemoteConfig/Sources/RCNConfigDefines.h"
  19. #import "FirebaseRemoteConfig/Sources/RCNConfigValue_Internal.h"
  20. #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  21. /// Using macro for securely preprocessing string concatenation in query before runtime.
  22. #define RCNTableNameMain "main"
  23. #define RCNTableNameMainActive "main_active"
  24. #define RCNTableNameMainDefault "main_default"
  25. #define RCNTableNameMetadata "fetch_metadata"
  26. #define RCNTableNameInternalMetadata "internal_metadata"
  27. #define RCNTableNameExperiment "experiment"
  28. static BOOL gIsNewDatabase;
  29. /// SQLite file name in versions 0, 1 and 2.
  30. static NSString *const RCNDatabaseName = @"RemoteConfig.sqlite3";
  31. /// The storage sub-directory that the Remote Config database resides in.
  32. static NSString *const RCNRemoteConfigStorageSubDirectory = @"Google/RemoteConfig";
  33. /// Remote Config database path for deprecated V0 version.
  34. static NSString *RemoteConfigPathForOldDatabaseV0() {
  35. NSArray *dirPaths =
  36. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  37. NSString *docPath = dirPaths.firstObject;
  38. return [docPath stringByAppendingPathComponent:RCNDatabaseName];
  39. }
  40. /// Remote Config database path for current database.
  41. static NSString *RemoteConfigPathForDatabase(void) {
  42. #if TARGET_OS_TV
  43. NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  44. #else
  45. NSArray *dirPaths =
  46. NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  47. #endif
  48. NSString *storageDirPath = dirPaths.firstObject;
  49. NSArray *components = @[ storageDirPath, RCNRemoteConfigStorageSubDirectory, RCNDatabaseName ];
  50. return [NSString pathWithComponents:components];
  51. }
  52. static BOOL RemoteConfigAddSkipBackupAttributeToItemAtPath(NSString *filePathString) {
  53. NSURL *URL = [NSURL fileURLWithPath:filePathString];
  54. assert([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]);
  55. NSError *error = nil;
  56. BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
  57. forKey:NSURLIsExcludedFromBackupKey
  58. error:&error];
  59. if (!success) {
  60. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000017", @"Error excluding %@ from backup %@.",
  61. [URL lastPathComponent], error);
  62. }
  63. return success;
  64. }
  65. static BOOL RemoteConfigCreateFilePathIfNotExist(NSString *filePath) {
  66. if (!filePath || !filePath.length) {
  67. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000018",
  68. @"Failed to create subdirectory for an empty file path.");
  69. return NO;
  70. }
  71. NSFileManager *fileManager = [NSFileManager defaultManager];
  72. if (![fileManager fileExistsAtPath:filePath]) {
  73. gIsNewDatabase = YES;
  74. NSError *error;
  75. [fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
  76. withIntermediateDirectories:YES
  77. attributes:nil
  78. error:&error];
  79. if (error) {
  80. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000019",
  81. @"Failed to create subdirectory for database file: %@.", error);
  82. return NO;
  83. }
  84. }
  85. return YES;
  86. }
  87. static NSArray *RemoteConfigMetadataTableColumnsInOrder() {
  88. return @[
  89. RCNKeyBundleIdentifier, RCNKeyFetchTime, RCNKeyDigestPerNamespace, RCNKeyDeviceContext,
  90. RCNKeyAppContext, RCNKeySuccessFetchTime, RCNKeyFailureFetchTime, RCNKeyLastFetchStatus,
  91. RCNKeyLastFetchError, RCNKeyLastApplyTime, RCNKeyLastSetDefaultsTime
  92. ];
  93. }
  94. @interface RCNConfigDBManager () {
  95. /// Database storing all the config information.
  96. sqlite3 *_database;
  97. /// Serial queue for database read/write operations.
  98. dispatch_queue_t _databaseOperationQueue;
  99. }
  100. @end
  101. @implementation RCNConfigDBManager
  102. + (instancetype)sharedInstance {
  103. static dispatch_once_t onceToken;
  104. static RCNConfigDBManager *sharedInstance;
  105. dispatch_once(&onceToken, ^{
  106. sharedInstance = [[RCNConfigDBManager alloc] init];
  107. });
  108. return sharedInstance;
  109. }
  110. /// Returns the current version of the Remote Config database.
  111. + (NSString *)remoteConfigPathForDatabase {
  112. return RemoteConfigPathForDatabase();
  113. }
  114. - (instancetype)init {
  115. self = [super init];
  116. if (self) {
  117. _databaseOperationQueue =
  118. dispatch_queue_create("com.google.GoogleConfigService.database", DISPATCH_QUEUE_SERIAL);
  119. [self createOrOpenDatabase];
  120. }
  121. return self;
  122. }
  123. #pragma mark - database
  124. - (void)migrateV1NamespaceToV2Namespace {
  125. for (int table = 0; table < 3; table++) {
  126. NSString *tableName = @"" RCNTableNameMain;
  127. switch (table) {
  128. case 1:
  129. tableName = @"" RCNTableNameMainActive;
  130. break;
  131. case 2:
  132. tableName = @"" RCNTableNameMainDefault;
  133. break;
  134. default:
  135. break;
  136. }
  137. NSString *SQLString = [NSString
  138. stringWithFormat:@"SELECT namespace FROM %@ WHERE namespace NOT LIKE '%%:%%'", tableName];
  139. const char *SQL = [SQLString UTF8String];
  140. sqlite3_stmt *statement = [self prepareSQL:SQL];
  141. if (!statement) {
  142. return;
  143. }
  144. NSMutableArray<NSString *> *namespaceArray = [[NSMutableArray alloc] init];
  145. while (sqlite3_step(statement) == SQLITE_ROW) {
  146. NSString *configNamespace =
  147. [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
  148. [namespaceArray addObject:configNamespace];
  149. }
  150. sqlite3_finalize(statement);
  151. // Update.
  152. for (NSString *namespaceToUpdate in namespaceArray) {
  153. NSString *newNamespace =
  154. [NSString stringWithFormat:@"%@:%@", namespaceToUpdate, kFIRDefaultAppName];
  155. NSString *updateSQLString =
  156. [NSString stringWithFormat:@"UPDATE %@ SET namespace = ? WHERE namespace = ?", tableName];
  157. const char *updateSQL = [updateSQLString UTF8String];
  158. sqlite3_stmt *updateStatement = [self prepareSQL:updateSQL];
  159. if (!updateStatement) {
  160. return;
  161. }
  162. NSArray<NSString *> *updateParams = @[ newNamespace, namespaceToUpdate ];
  163. [self bindStringsToStatement:updateStatement stringArray:updateParams];
  164. int result = sqlite3_step(updateStatement);
  165. if (result != SQLITE_DONE) {
  166. [self logErrorWithSQL:SQL finalizeStatement:updateStatement returnValue:NO];
  167. return;
  168. }
  169. sqlite3_finalize(updateStatement);
  170. }
  171. }
  172. }
  173. - (void)createOrOpenDatabase {
  174. __weak RCNConfigDBManager *weakSelf = self;
  175. dispatch_async(_databaseOperationQueue, ^{
  176. RCNConfigDBManager *strongSelf = weakSelf;
  177. if (!strongSelf) {
  178. return;
  179. }
  180. NSString *oldV0DBPath = RemoteConfigPathForOldDatabaseV0();
  181. // Backward Compatibility
  182. if ([[NSFileManager defaultManager] fileExistsAtPath:oldV0DBPath]) {
  183. FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000009",
  184. @"Old database V0 exists, removed it and replace with the new one.");
  185. [strongSelf removeDatabase:oldV0DBPath];
  186. }
  187. NSString *dbPath = [RCNConfigDBManager remoteConfigPathForDatabase];
  188. FIRLogInfo(kFIRLoggerRemoteConfig, @"I-RCN000062", @"Loading database at path %@", dbPath);
  189. const char *databasePath = dbPath.UTF8String;
  190. // Create or open database path.
  191. if (!RemoteConfigCreateFilePathIfNotExist(dbPath)) {
  192. return;
  193. }
  194. int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FILEPROTECTION_COMPLETE |
  195. SQLITE_OPEN_FULLMUTEX;
  196. if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) {
  197. // Always try to create table if not exists for backward compatibility.
  198. if (![strongSelf createTableSchema]) {
  199. // Remove database before fail.
  200. [strongSelf removeDatabase:dbPath];
  201. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000010", @"Failed to create table.");
  202. // Create a new database if existing database file is corrupted.
  203. if (!RemoteConfigCreateFilePathIfNotExist(dbPath)) {
  204. return;
  205. }
  206. if (sqlite3_open_v2(databasePath, &strongSelf->_database, flags, NULL) == SQLITE_OK) {
  207. if (![strongSelf createTableSchema]) {
  208. // Remove database before fail.
  209. [strongSelf removeDatabase:dbPath];
  210. // If it failed again, there's nothing we can do here.
  211. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000010", @"Failed to create table.");
  212. } else {
  213. // Exclude the app data used from iCloud backup.
  214. RemoteConfigAddSkipBackupAttributeToItemAtPath(dbPath);
  215. }
  216. } else {
  217. [strongSelf logDatabaseError];
  218. }
  219. } else {
  220. // DB file already exists. Migrate any V1 namespace column entries to V2 fully qualified
  221. // 'namespace:FIRApp' entries.
  222. [self migrateV1NamespaceToV2Namespace];
  223. // Exclude the app data used from iCloud backup.
  224. RemoteConfigAddSkipBackupAttributeToItemAtPath(dbPath);
  225. }
  226. } else {
  227. [strongSelf logDatabaseError];
  228. }
  229. });
  230. }
  231. - (BOOL)createTableSchema {
  232. RCN_MUST_NOT_BE_MAIN_THREAD();
  233. static const char *createTableMain =
  234. "create TABLE IF NOT EXISTS " RCNTableNameMain
  235. " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)";
  236. static const char *createTableMainActive =
  237. "create TABLE IF NOT EXISTS " RCNTableNameMainActive
  238. " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)";
  239. static const char *createTableMainDefault =
  240. "create TABLE IF NOT EXISTS " RCNTableNameMainDefault
  241. " (_id INTEGER PRIMARY KEY, bundle_identifier TEXT, namespace TEXT, key TEXT, value BLOB)";
  242. static const char *createTableMetadata =
  243. "create TABLE IF NOT EXISTS " RCNTableNameMetadata
  244. " (_id INTEGER PRIMARY KEY, bundle_identifier"
  245. " TEXT, fetch_time INTEGER, digest_per_ns BLOB, device_context BLOB, app_context BLOB, "
  246. "success_fetch_time BLOB, failure_fetch_time BLOB, last_fetch_status INTEGER, "
  247. "last_fetch_error INTEGER, last_apply_time INTEGER, last_set_defaults_time INTEGER)";
  248. static const char *createTableInternalMetadata =
  249. "create TABLE IF NOT EXISTS " RCNTableNameInternalMetadata
  250. " (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)";
  251. static const char *createTableExperiment = "create TABLE IF NOT EXISTS " RCNTableNameExperiment
  252. " (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)";
  253. return [self executeQuery:createTableMain] && [self executeQuery:createTableMainActive] &&
  254. [self executeQuery:createTableMainDefault] && [self executeQuery:createTableMetadata] &&
  255. [self executeQuery:createTableInternalMetadata] &&
  256. [self executeQuery:createTableExperiment];
  257. }
  258. - (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path {
  259. __weak RCNConfigDBManager *weakSelf = self;
  260. dispatch_sync(_databaseOperationQueue, ^{
  261. RCNConfigDBManager *strongSelf = weakSelf;
  262. if (!strongSelf) {
  263. return;
  264. }
  265. if (sqlite3_close(strongSelf->_database) != SQLITE_OK) {
  266. [self logDatabaseError];
  267. }
  268. strongSelf->_database = nil;
  269. NSFileManager *fileManager = [NSFileManager defaultManager];
  270. NSError *error;
  271. if (![fileManager removeItemAtPath:path error:&error]) {
  272. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011",
  273. @"Failed to remove database at path %@ for error %@.", path, error);
  274. }
  275. });
  276. }
  277. - (void)removeDatabase:(NSString *)path {
  278. if (sqlite3_close(_database) != SQLITE_OK) {
  279. [self logDatabaseError];
  280. }
  281. _database = nil;
  282. NSFileManager *fileManager = [NSFileManager defaultManager];
  283. NSError *error;
  284. if (![fileManager removeItemAtPath:path error:&error]) {
  285. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011",
  286. @"Failed to remove database at path %@ for error %@.", path, error);
  287. }
  288. }
  289. #pragma mark - execute
  290. - (BOOL)executeQuery:(const char *)SQL {
  291. RCN_MUST_NOT_BE_MAIN_THREAD();
  292. char *error;
  293. if (sqlite3_exec(_database, SQL, nil, nil, &error) != SQLITE_OK) {
  294. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000012", @"Failed to execute query with error %s.",
  295. error);
  296. return NO;
  297. }
  298. return YES;
  299. }
  300. #pragma mark - insert
  301. - (void)insertMetadataTableWithValues:(NSDictionary *)columnNameToValue
  302. completionHandler:(RCNDBCompletion)handler {
  303. __weak RCNConfigDBManager *weakSelf = self;
  304. dispatch_async(_databaseOperationQueue, ^{
  305. BOOL success = [weakSelf insertMetadataTableWithValues:columnNameToValue];
  306. if (handler) {
  307. dispatch_async(dispatch_get_main_queue(), ^{
  308. handler(success, nil);
  309. });
  310. }
  311. });
  312. }
  313. - (BOOL)insertMetadataTableWithValues:(NSDictionary *)columnNameToValue {
  314. RCN_MUST_NOT_BE_MAIN_THREAD();
  315. static const char *SQL =
  316. "INSERT INTO " RCNTableNameMetadata
  317. " (bundle_identifier, fetch_time, digest_per_ns, device_context, "
  318. "app_context, success_fetch_time, failure_fetch_time, last_fetch_status, "
  319. "last_fetch_error, last_apply_time, last_set_defaults_time) values (?, ?, ?, ?, ?, "
  320. "?, ?, ?, ?, ?, ?)";
  321. sqlite3_stmt *statement = [self prepareSQL:SQL];
  322. if (!statement) {
  323. [self logErrorWithSQL:SQL finalizeStatement:nil returnValue:NO];
  324. return NO;
  325. }
  326. NSArray *columns = RemoteConfigMetadataTableColumnsInOrder();
  327. int index = 0;
  328. for (NSString *columnName in columns) {
  329. if ([columnName isEqualToString:RCNKeyBundleIdentifier]) {
  330. NSString *value = columnNameToValue[columnName];
  331. if (![self bindStringToStatement:statement index:++index string:value]) {
  332. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  333. }
  334. } else if ([columnName isEqualToString:RCNKeyFetchTime] ||
  335. [columnName isEqualToString:RCNKeyLastApplyTime] ||
  336. [columnName isEqualToString:RCNKeyLastSetDefaultsTime]) {
  337. double value = [columnNameToValue[columnName] doubleValue];
  338. if (sqlite3_bind_double(statement, ++index, value) != SQLITE_OK) {
  339. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  340. }
  341. } else if ([columnName isEqualToString:RCNKeyLastFetchStatus] ||
  342. [columnName isEqualToString:RCNKeyLastFetchError]) {
  343. int value = [columnNameToValue[columnName] intValue];
  344. if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) {
  345. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  346. }
  347. } else {
  348. NSData *data = columnNameToValue[columnName];
  349. if (sqlite3_bind_blob(statement, ++index, data.bytes, (int)data.length, NULL) != SQLITE_OK) {
  350. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  351. }
  352. }
  353. }
  354. if (sqlite3_step(statement) != SQLITE_DONE) {
  355. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  356. }
  357. sqlite3_finalize(statement);
  358. return YES;
  359. }
  360. - (void)insertMainTableWithValues:(NSArray *)values
  361. fromSource:(RCNDBSource)source
  362. completionHandler:(RCNDBCompletion)handler {
  363. __weak RCNConfigDBManager *weakSelf = self;
  364. dispatch_async(_databaseOperationQueue, ^{
  365. BOOL success = [weakSelf insertMainTableWithValues:values fromSource:source];
  366. if (handler) {
  367. dispatch_async(dispatch_get_main_queue(), ^{
  368. handler(success, nil);
  369. });
  370. }
  371. });
  372. }
  373. - (BOOL)insertMainTableWithValues:(NSArray *)values fromSource:(RCNDBSource)source {
  374. RCN_MUST_NOT_BE_MAIN_THREAD();
  375. if (values.count != 4) {
  376. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000013",
  377. @"Failed to insert config record. Wrong number of give parameters, current "
  378. @"number is %ld, correct number is 4.",
  379. (long)values.count);
  380. return NO;
  381. }
  382. const char *SQL = "INSERT INTO " RCNTableNameMain
  383. " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)";
  384. if (source == RCNDBSourceDefault) {
  385. SQL = "INSERT INTO " RCNTableNameMainDefault
  386. " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)";
  387. } else if (source == RCNDBSourceActive) {
  388. SQL = "INSERT INTO " RCNTableNameMainActive
  389. " (bundle_identifier, namespace, key, value) values (?, ?, ?, ?)";
  390. }
  391. sqlite3_stmt *statement = [self prepareSQL:SQL];
  392. if (!statement) {
  393. return NO;
  394. }
  395. NSString *aString = values[0];
  396. if (![self bindStringToStatement:statement index:1 string:aString]) {
  397. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  398. }
  399. aString = values[1];
  400. if (![self bindStringToStatement:statement index:2 string:aString]) {
  401. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  402. }
  403. aString = values[2];
  404. if (![self bindStringToStatement:statement index:3 string:aString]) {
  405. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  406. }
  407. NSData *blobData = values[3];
  408. if (sqlite3_bind_blob(statement, 4, blobData.bytes, (int)blobData.length, NULL) != SQLITE_OK) {
  409. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  410. }
  411. if (sqlite3_step(statement) != SQLITE_DONE) {
  412. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  413. }
  414. sqlite3_finalize(statement);
  415. return YES;
  416. }
  417. - (void)insertInternalMetadataTableWithValues:(NSArray *)values
  418. completionHandler:(RCNDBCompletion)handler {
  419. __weak RCNConfigDBManager *weakSelf = self;
  420. dispatch_async(_databaseOperationQueue, ^{
  421. BOOL success = [weakSelf insertInternalMetadataWithValues:values];
  422. if (handler) {
  423. dispatch_async(dispatch_get_main_queue(), ^{
  424. handler(success, nil);
  425. });
  426. }
  427. });
  428. }
  429. - (BOOL)insertInternalMetadataWithValues:(NSArray *)values {
  430. RCN_MUST_NOT_BE_MAIN_THREAD();
  431. if (values.count != 2) {
  432. return NO;
  433. }
  434. const char *SQL =
  435. "INSERT OR REPLACE INTO " RCNTableNameInternalMetadata " (key, value) values (?, ?)";
  436. sqlite3_stmt *statement = [self prepareSQL:SQL];
  437. if (!statement) {
  438. return NO;
  439. }
  440. NSString *aString = values[0];
  441. if (![self bindStringToStatement:statement index:1 string:aString]) {
  442. [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  443. return NO;
  444. }
  445. NSData *blobData = values[1];
  446. if (sqlite3_bind_blob(statement, 2, blobData.bytes, (int)blobData.length, NULL) != SQLITE_OK) {
  447. [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  448. return NO;
  449. }
  450. if (sqlite3_step(statement) != SQLITE_DONE) {
  451. [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  452. return NO;
  453. }
  454. sqlite3_finalize(statement);
  455. return YES;
  456. }
  457. - (void)insertExperimentTableWithKey:(NSString *)key
  458. value:(NSData *)serializedValue
  459. completionHandler:(RCNDBCompletion)handler {
  460. dispatch_async(_databaseOperationQueue, ^{
  461. BOOL success = [self insertExperimentTableWithKey:key value:serializedValue];
  462. if (handler) {
  463. dispatch_async(dispatch_get_main_queue(), ^{
  464. handler(success, nil);
  465. });
  466. }
  467. });
  468. }
  469. - (BOOL)insertExperimentTableWithKey:(NSString *)key value:(NSData *)dataValue {
  470. if ([key isEqualToString:@RCNExperimentTableKeyMetadata]) {
  471. return [self updateExperimentMetadata:dataValue];
  472. }
  473. RCN_MUST_NOT_BE_MAIN_THREAD();
  474. const char *SQL = "INSERT INTO " RCNTableNameExperiment " (key, value) values (?, ?)";
  475. sqlite3_stmt *statement = [self prepareSQL:SQL];
  476. if (!statement) {
  477. return NO;
  478. }
  479. if (![self bindStringToStatement:statement index:1 string:key]) {
  480. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  481. }
  482. if (sqlite3_bind_blob(statement, 2, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) {
  483. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  484. }
  485. if (sqlite3_step(statement) != SQLITE_DONE) {
  486. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  487. }
  488. sqlite3_finalize(statement);
  489. return YES;
  490. }
  491. - (BOOL)updateExperimentMetadata:(NSData *)dataValue {
  492. RCN_MUST_NOT_BE_MAIN_THREAD();
  493. const char *SQL = "INSERT OR REPLACE INTO " RCNTableNameExperiment
  494. " (_id, key, value) values ((SELECT _id from " RCNTableNameExperiment
  495. " WHERE key = ?), ?, ?)";
  496. sqlite3_stmt *statement = [self prepareSQL:SQL];
  497. if (!statement) {
  498. return NO;
  499. }
  500. if (![self bindStringToStatement:statement index:1 string:@RCNExperimentTableKeyMetadata]) {
  501. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  502. }
  503. if (![self bindStringToStatement:statement index:2 string:@RCNExperimentTableKeyMetadata]) {
  504. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  505. }
  506. if (sqlite3_bind_blob(statement, 3, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) {
  507. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  508. }
  509. if (sqlite3_step(statement) != SQLITE_DONE) {
  510. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  511. }
  512. sqlite3_finalize(statement);
  513. return YES;
  514. }
  515. #pragma mark - update
  516. - (void)updateMetadataWithOption:(RCNUpdateOption)option
  517. values:(NSArray *)values
  518. completionHandler:(RCNDBCompletion)handler {
  519. dispatch_async(_databaseOperationQueue, ^{
  520. BOOL success = [self updateMetadataTableWithOption:option andValues:values];
  521. if (handler) {
  522. dispatch_async(dispatch_get_main_queue(), ^{
  523. handler(success, nil);
  524. });
  525. }
  526. });
  527. }
  528. - (BOOL)updateMetadataTableWithOption:(RCNUpdateOption)option andValues:(NSArray *)values {
  529. RCN_MUST_NOT_BE_MAIN_THREAD();
  530. static const char *SQL =
  531. "UPDATE " RCNTableNameMetadata " (last_fetch_status, last_fetch_error, last_apply_time, "
  532. "last_set_defaults_time) values (?, ?, ?, ?)";
  533. if (option == RCNUpdateOptionFetchStatus) {
  534. SQL = "UPDATE " RCNTableNameMetadata " SET last_fetch_status = ?, last_fetch_error = ?";
  535. } else if (option == RCNUpdateOptionApplyTime) {
  536. SQL = "UPDATE " RCNTableNameMetadata " SET last_apply_time = ?";
  537. } else if (option == RCNUpdateOptionDefaultTime) {
  538. SQL = "UPDATE " RCNTableNameMetadata " SET last_set_defaults_time = ?";
  539. } else {
  540. return NO;
  541. }
  542. sqlite3_stmt *statement = [self prepareSQL:SQL];
  543. if (!statement) {
  544. return NO;
  545. }
  546. int index = 0;
  547. if ((option == RCNUpdateOptionApplyTime || option == RCNUpdateOptionDefaultTime) &&
  548. values.count == 1) {
  549. double value = [values[0] doubleValue];
  550. if (sqlite3_bind_double(statement, ++index, value) != SQLITE_OK) {
  551. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  552. }
  553. } else if (option == RCNUpdateOptionFetchStatus && values.count == 2) {
  554. int value = [values[0] intValue];
  555. if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) {
  556. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  557. }
  558. value = [values[1] intValue];
  559. if (sqlite3_bind_int(statement, ++index, value) != SQLITE_OK) {
  560. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  561. }
  562. }
  563. if (sqlite3_step(statement) != SQLITE_DONE) {
  564. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  565. }
  566. sqlite3_finalize(statement);
  567. return YES;
  568. }
  569. #pragma mark - read from DB
  570. - (NSDictionary *)loadMetadataWithBundleIdentifier:(NSString *)bundleIdentifier {
  571. __block NSDictionary *metadataTableResult;
  572. __weak RCNConfigDBManager *weakSelf = self;
  573. dispatch_sync(_databaseOperationQueue, ^{
  574. metadataTableResult = [weakSelf loadMetadataTableWithBundleIdentifier:bundleIdentifier];
  575. });
  576. if (metadataTableResult) {
  577. return metadataTableResult;
  578. }
  579. return [[NSDictionary alloc] init];
  580. }
  581. - (NSMutableDictionary *)loadMetadataTableWithBundleIdentifier:(NSString *)bundleIdentifier {
  582. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  583. const char *SQL =
  584. "SELECT bundle_identifier, fetch_time, digest_per_ns, device_context, app_context, "
  585. "success_fetch_time, failure_fetch_time , last_fetch_status, "
  586. "last_fetch_error, last_apply_time, last_set_defaults_time FROM " RCNTableNameMetadata
  587. " WHERE bundle_identifier = ?";
  588. sqlite3_stmt *statement = [self prepareSQL:SQL];
  589. if (!statement) {
  590. return nil;
  591. }
  592. NSArray *params = @[ bundleIdentifier ];
  593. [self bindStringsToStatement:statement stringArray:params];
  594. while (sqlite3_step(statement) == SQLITE_ROW) {
  595. NSString *dbBundleIdentifier =
  596. [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
  597. if (dbBundleIdentifier && ![dbBundleIdentifier isEqualToString:bundleIdentifier]) {
  598. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000014",
  599. @"Load Metadata from table error: Wrong package name %@, should be %@.",
  600. dbBundleIdentifier, bundleIdentifier);
  601. return nil;
  602. }
  603. double fetchTime = sqlite3_column_double(statement, 1);
  604. NSData *digestPerNamespace = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 2)
  605. length:sqlite3_column_bytes(statement, 2)];
  606. NSData *deviceContext = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 3)
  607. length:sqlite3_column_bytes(statement, 3)];
  608. NSData *appContext = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 4)
  609. length:sqlite3_column_bytes(statement, 4)];
  610. NSData *successTimeDigest = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 5)
  611. length:sqlite3_column_bytes(statement, 5)];
  612. NSData *failureTimeDigest = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 6)
  613. length:sqlite3_column_bytes(statement, 6)];
  614. int lastFetchStatus = sqlite3_column_int(statement, 7);
  615. int lastFetchFailReason = sqlite3_column_int(statement, 8);
  616. double lastApplyTimestamp = sqlite3_column_double(statement, 9);
  617. double lastSetDefaultsTimestamp = sqlite3_column_double(statement, 10);
  618. NSError *error;
  619. NSMutableDictionary *deviceContextDict = nil;
  620. if (deviceContext) {
  621. deviceContextDict = [NSJSONSerialization JSONObjectWithData:deviceContext
  622. options:NSJSONReadingMutableContainers
  623. error:&error];
  624. }
  625. NSMutableDictionary *appContextDict = nil;
  626. if (appContext) {
  627. appContextDict = [NSJSONSerialization JSONObjectWithData:appContext
  628. options:NSJSONReadingMutableContainers
  629. error:&error];
  630. }
  631. NSMutableDictionary<NSString *, id> *digestPerNamespaceDictionary = nil;
  632. if (digestPerNamespace) {
  633. digestPerNamespaceDictionary =
  634. [NSJSONSerialization JSONObjectWithData:digestPerNamespace
  635. options:NSJSONReadingMutableContainers
  636. error:&error];
  637. }
  638. NSMutableArray *successTimes = nil;
  639. if (successTimeDigest) {
  640. successTimes = [NSJSONSerialization JSONObjectWithData:successTimeDigest
  641. options:NSJSONReadingMutableContainers
  642. error:&error];
  643. }
  644. NSMutableArray *failureTimes = nil;
  645. if (failureTimeDigest) {
  646. failureTimes = [NSJSONSerialization JSONObjectWithData:failureTimeDigest
  647. options:NSJSONReadingMutableContainers
  648. error:&error];
  649. }
  650. dict[RCNKeyBundleIdentifier] = bundleIdentifier;
  651. dict[RCNKeyFetchTime] = @(fetchTime);
  652. dict[RCNKeyDigestPerNamespace] = digestPerNamespaceDictionary;
  653. dict[RCNKeyDeviceContext] = deviceContextDict;
  654. dict[RCNKeyAppContext] = appContextDict;
  655. dict[RCNKeySuccessFetchTime] = successTimes;
  656. dict[RCNKeyFailureFetchTime] = failureTimes;
  657. dict[RCNKeyLastFetchStatus] = @(lastFetchStatus);
  658. dict[RCNKeyLastFetchError] = @(lastFetchFailReason);
  659. dict[RCNKeyLastApplyTime] = @(lastApplyTimestamp);
  660. dict[RCNKeyLastSetDefaultsTime] = @(lastSetDefaultsTimestamp);
  661. break;
  662. }
  663. sqlite3_finalize(statement);
  664. return dict;
  665. }
  666. - (void)loadExperimentWithCompletionHandler:(RCNDBCompletion)handler {
  667. __weak RCNConfigDBManager *weakSelf = self;
  668. dispatch_async(_databaseOperationQueue, ^{
  669. RCNConfigDBManager *strongSelf = weakSelf;
  670. if (!strongSelf) {
  671. return;
  672. }
  673. NSMutableArray *experimentPayloads =
  674. [strongSelf loadExperimentTableFromKey:@RCNExperimentTableKeyPayload];
  675. if (!experimentPayloads) {
  676. experimentPayloads = [[NSMutableArray alloc] init];
  677. }
  678. NSMutableDictionary *experimentMetadata;
  679. NSMutableArray *experiments =
  680. [strongSelf loadExperimentTableFromKey:@RCNExperimentTableKeyMetadata];
  681. // There should be only one entry for experiment metadata.
  682. if (experiments.count > 0) {
  683. NSError *error;
  684. experimentMetadata = [NSJSONSerialization JSONObjectWithData:experiments[0]
  685. options:NSJSONReadingMutableContainers
  686. error:&error];
  687. }
  688. if (!experimentMetadata) {
  689. experimentMetadata = [[NSMutableDictionary alloc] init];
  690. }
  691. if (handler) {
  692. dispatch_async(dispatch_get_main_queue(), ^{
  693. handler(
  694. YES, @{
  695. @RCNExperimentTableKeyPayload : [experimentPayloads copy],
  696. @RCNExperimentTableKeyMetadata : [experimentMetadata copy]
  697. });
  698. });
  699. }
  700. });
  701. }
  702. - (NSMutableArray<NSData *> *)loadExperimentTableFromKey:(NSString *)key {
  703. RCN_MUST_NOT_BE_MAIN_THREAD();
  704. NSMutableArray *results = [[NSMutableArray alloc] init];
  705. const char *SQL = "SELECT value FROM " RCNTableNameExperiment " WHERE key = ?";
  706. sqlite3_stmt *statement = [self prepareSQL:SQL];
  707. if (!statement) {
  708. return nil;
  709. }
  710. NSArray *params = @[ key ];
  711. [self bindStringsToStatement:statement stringArray:params];
  712. NSData *experimentData;
  713. while (sqlite3_step(statement) == SQLITE_ROW) {
  714. experimentData = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0)
  715. length:sqlite3_column_bytes(statement, 0)];
  716. if (experimentData) {
  717. [results addObject:experimentData];
  718. }
  719. }
  720. sqlite3_finalize(statement);
  721. return results;
  722. }
  723. - (NSDictionary *)loadInternalMetadataTable {
  724. __block NSMutableDictionary *internalMetadataTableResult;
  725. __weak RCNConfigDBManager *weakSelf = self;
  726. dispatch_sync(_databaseOperationQueue, ^{
  727. internalMetadataTableResult = [weakSelf loadInternalMetadataTableInternal];
  728. });
  729. return internalMetadataTableResult;
  730. }
  731. - (NSMutableDictionary *)loadInternalMetadataTableInternal {
  732. NSMutableDictionary *internalMetadata = [[NSMutableDictionary alloc] init];
  733. const char *SQL = "SELECT key, value FROM " RCNTableNameInternalMetadata;
  734. sqlite3_stmt *statement = [self prepareSQL:SQL];
  735. if (!statement) {
  736. return nil;
  737. }
  738. while (sqlite3_step(statement) == SQLITE_ROW) {
  739. NSString *key = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
  740. NSData *dataValue = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 1)
  741. length:sqlite3_column_bytes(statement, 1)];
  742. internalMetadata[key] = dataValue;
  743. }
  744. sqlite3_finalize(statement);
  745. return internalMetadata;
  746. }
  747. /// This method is only meant to be called at init time. The underlying logic will need to be
  748. /// revaluated if the assumption changes at a later time.
  749. - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
  750. completionHandler:(RCNDBLoadCompletion)handler {
  751. __weak RCNConfigDBManager *weakSelf = self;
  752. dispatch_async(_databaseOperationQueue, ^{
  753. RCNConfigDBManager *strongSelf = weakSelf;
  754. if (!strongSelf) {
  755. return;
  756. }
  757. __block NSDictionary *fetchedConfig =
  758. [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier
  759. fromSource:RCNDBSourceFetched];
  760. __block NSDictionary *activeConfig =
  761. [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier
  762. fromSource:RCNDBSourceActive];
  763. __block NSDictionary *defaultConfig =
  764. [strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier
  765. fromSource:RCNDBSourceDefault];
  766. if (handler) {
  767. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  768. fetchedConfig = fetchedConfig ? fetchedConfig : [[NSDictionary alloc] init];
  769. activeConfig = activeConfig ? activeConfig : [[NSDictionary alloc] init];
  770. defaultConfig = defaultConfig ? defaultConfig : [[NSDictionary alloc] init];
  771. handler(YES, fetchedConfig, activeConfig, defaultConfig);
  772. });
  773. }
  774. });
  775. }
  776. - (NSMutableDictionary *)loadMainTableWithBundleIdentifier:(NSString *)bundleIdentifier
  777. fromSource:(RCNDBSource)source {
  778. NSMutableDictionary *namespaceToConfig = [[NSMutableDictionary alloc] init];
  779. const char *SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMain
  780. " WHERE bundle_identifier = ?";
  781. if (source == RCNDBSourceDefault) {
  782. SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMainDefault
  783. " WHERE bundle_identifier = ?";
  784. } else if (source == RCNDBSourceActive) {
  785. SQL = "SELECT bundle_identifier, namespace, key, value FROM " RCNTableNameMainActive
  786. " WHERE bundle_identifier = ?";
  787. }
  788. NSArray *params = @[ bundleIdentifier ];
  789. sqlite3_stmt *statement = [self prepareSQL:SQL];
  790. if (!statement) {
  791. return nil;
  792. }
  793. [self bindStringsToStatement:statement stringArray:params];
  794. while (sqlite3_step(statement) == SQLITE_ROW) {
  795. NSString *configNamespace =
  796. [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
  797. NSString *key = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
  798. NSData *value = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 3)
  799. length:sqlite3_column_bytes(statement, 3)];
  800. if (!namespaceToConfig[configNamespace]) {
  801. namespaceToConfig[configNamespace] = [[NSMutableDictionary alloc] init];
  802. }
  803. if (source == RCNDBSourceDefault) {
  804. namespaceToConfig[configNamespace][key] =
  805. [[FIRRemoteConfigValue alloc] initWithData:value source:FIRRemoteConfigSourceDefault];
  806. } else {
  807. namespaceToConfig[configNamespace][key] =
  808. [[FIRRemoteConfigValue alloc] initWithData:value source:FIRRemoteConfigSourceRemote];
  809. }
  810. }
  811. sqlite3_finalize(statement);
  812. return namespaceToConfig;
  813. }
  814. #pragma mark - delete
  815. - (void)deleteRecordFromMainTableWithNamespace:(NSString *)namespace_p
  816. bundleIdentifier:(NSString *)bundleIdentifier
  817. fromSource:(RCNDBSource)source {
  818. __weak RCNConfigDBManager *weakSelf = self;
  819. dispatch_async(_databaseOperationQueue, ^{
  820. RCNConfigDBManager *strongSelf = weakSelf;
  821. if (!strongSelf) {
  822. return;
  823. }
  824. NSArray *params = @[ bundleIdentifier, namespace_p ];
  825. const char *SQL =
  826. "DELETE FROM " RCNTableNameMain " WHERE bundle_identifier = ? and namespace = ?";
  827. if (source == RCNDBSourceDefault) {
  828. SQL = "DELETE FROM " RCNTableNameMainDefault " WHERE bundle_identifier = ? and namespace = ?";
  829. } else if (source == RCNDBSourceActive) {
  830. SQL = "DELETE FROM " RCNTableNameMainActive " WHERE bundle_identifier = ? and namespace = ?";
  831. }
  832. [strongSelf executeQuery:SQL withParams:params];
  833. });
  834. }
  835. - (void)deleteRecordWithBundleIdentifier:(NSString *)bundleIdentifier
  836. isInternalDB:(BOOL)isInternalDB {
  837. __weak RCNConfigDBManager *weakSelf = self;
  838. dispatch_async(_databaseOperationQueue, ^{
  839. RCNConfigDBManager *strongSelf = weakSelf;
  840. if (!strongSelf) {
  841. return;
  842. }
  843. const char *SQL = "DELETE FROM " RCNTableNameInternalMetadata " WHERE key LIKE ?";
  844. if (!isInternalDB) {
  845. SQL = "DELETE FROM " RCNTableNameMetadata " WHERE bundle_identifier = ?";
  846. }
  847. NSArray *params = @[ bundleIdentifier ];
  848. [strongSelf executeQuery:SQL withParams:params];
  849. });
  850. }
  851. - (void)deleteAllRecordsFromTableWithSource:(RCNDBSource)source {
  852. __weak RCNConfigDBManager *weakSelf = self;
  853. dispatch_async(_databaseOperationQueue, ^{
  854. RCNConfigDBManager *strongSelf = weakSelf;
  855. if (!strongSelf) {
  856. return;
  857. }
  858. const char *SQL = "DELETE FROM " RCNTableNameMain;
  859. if (source == RCNDBSourceDefault) {
  860. SQL = "DELETE FROM " RCNTableNameMainDefault;
  861. } else if (source == RCNDBSourceActive) {
  862. SQL = "DELETE FROM " RCNTableNameMainActive;
  863. }
  864. [strongSelf executeQuery:SQL];
  865. });
  866. }
  867. - (void)deleteExperimentTableForKey:(NSString *)key {
  868. __weak RCNConfigDBManager *weakSelf = self;
  869. dispatch_async(_databaseOperationQueue, ^{
  870. RCNConfigDBManager *strongSelf = weakSelf;
  871. if (!strongSelf) {
  872. return;
  873. }
  874. NSArray *params = @[ key ];
  875. const char *SQL = "DELETE FROM " RCNTableNameExperiment " WHERE key = ?";
  876. [strongSelf executeQuery:SQL withParams:params];
  877. });
  878. }
  879. #pragma mark - helper
  880. - (BOOL)executeQuery:(const char *)SQL withParams:(NSArray *)params {
  881. RCN_MUST_NOT_BE_MAIN_THREAD();
  882. sqlite3_stmt *statement = [self prepareSQL:SQL];
  883. if (!statement) {
  884. return NO;
  885. }
  886. [self bindStringsToStatement:statement stringArray:params];
  887. if (sqlite3_step(statement) != SQLITE_DONE) {
  888. return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  889. }
  890. sqlite3_finalize(statement);
  891. return YES;
  892. }
  893. /// Params only accept TEXT format string.
  894. - (BOOL)bindStringsToStatement:(sqlite3_stmt *)statement stringArray:(NSArray *)array {
  895. int index = 1;
  896. for (NSString *param in array) {
  897. if (![self bindStringToStatement:statement index:index string:param]) {
  898. return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO];
  899. }
  900. index++;
  901. }
  902. return YES;
  903. }
  904. - (BOOL)bindStringToStatement:(sqlite3_stmt *)statement index:(int)index string:(NSString *)value {
  905. if (sqlite3_bind_text(statement, index, [value UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
  906. return [self logErrorWithSQL:nil finalizeStatement:statement returnValue:NO];
  907. }
  908. return YES;
  909. }
  910. - (sqlite3_stmt *)prepareSQL:(const char *)SQL {
  911. sqlite3_stmt *statement = nil;
  912. if (sqlite3_prepare_v2(_database, SQL, -1, &statement, NULL) != SQLITE_OK) {
  913. [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
  914. return nil;
  915. }
  916. return statement;
  917. }
  918. - (NSString *)errorMessage {
  919. return [NSString stringWithFormat:@"%s", sqlite3_errmsg(_database)];
  920. }
  921. - (int)errorCode {
  922. return sqlite3_errcode(_database);
  923. }
  924. - (void)logDatabaseError {
  925. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000015", @"Error message: %@. Error code: %d.",
  926. [self errorMessage], [self errorCode]);
  927. }
  928. - (BOOL)logErrorWithSQL:(const char *)SQL
  929. finalizeStatement:(sqlite3_stmt *)statement
  930. returnValue:(BOOL)returnValue {
  931. if (SQL) {
  932. FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000016", @"Failed with SQL: %s.", SQL);
  933. }
  934. [self logDatabaseError];
  935. if (statement) {
  936. sqlite3_finalize(statement);
  937. }
  938. return returnValue;
  939. }
  940. - (BOOL)isNewDatabase {
  941. return gIsNewDatabase;
  942. }
  943. @end