RCNConfigDBManager.m 50 KB

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