RCNConfigDBManager.m 50 KB

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