RCNConfigDBManager.m 44 KB

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