RCNConfigDBManager.m 48 KB

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