From 15d7d0780971fbe9f2a5d4823ba43b10028e73fb Mon Sep 17 00:00:00 2001 From: Les Melnychuk Date: Tue, 9 Jul 2019 13:22:31 +0300 Subject: [PATCH] Add ability to run SQL queries --- .../DatabaseBrowser/FLEXDatabaseManager.h | 11 +- .../FLEXRealmDatabaseManager.m | 5 +- .../DatabaseBrowser/FLEXSQLResult.h | 41 +++++++ .../DatabaseBrowser/FLEXSQLResult.m | 47 ++++++++ .../FLEXSQLiteDatabaseManager.m | 108 +++++++++--------- .../FLEXTableContentViewController.h | 4 +- .../FLEXTableContentViewController.m | 27 +++-- .../FLEXTableListViewController.m | 67 ++++++++--- FLEX.xcodeproj/project.pbxproj | 8 ++ 9 files changed, 232 insertions(+), 86 deletions(-) create mode 100644 Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.h create mode 100644 Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.m diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h index bc0dbc3d5c..810cb73ed8 100644 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h @@ -12,16 +12,23 @@ // which Flying Meat Inc. licenses this file to you. #import +#import "FLEXSQLResult.h" @protocol FLEXDatabaseManager @required + + (instancetype)managerForDatabase:(NSString *)path; - (BOOL)open; + /// @return a list of all table names - (NSArray *)queryAllTables; -- (NSArray *)queryAllColumnsWithTableName:(NSString *)tableName; -- (NSArray *)queryAllDataWithTableName:(NSString *)tableName; +- (NSArray *)queryAllColumnsOfTable:(NSString *)tableName; +- (NSArray *)queryAllDataInTable:(NSString *)tableName; + +@optional + +- (FLEXSQLResult *)executeStatement:(NSString *)SQLStatement; @end diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m index 5eba85bd29..772c8505f0 100644 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m @@ -8,6 +8,7 @@ #import "FLEXRealmDatabaseManager.h" #import "NSArray+Functional.h" +#import "FLEXSQLResult.h" #if __has_include() #import @@ -68,7 +69,7 @@ - (BOOL)open { }]; } -- (NSArray *)queryAllColumnsWithTableName:(NSString *)tableName { +- (NSArray *)queryAllColumnsOfTable:(NSString *)tableName { RLMObjectSchema *objectSchema = [self.realm.schema schemaForClassName:tableName]; // Map each column to its name return [objectSchema.properties flex_mapped:^id(RLMProperty *property, NSUInteger idx) { @@ -76,7 +77,7 @@ - (BOOL)open { }]; } -- (NSArray *)queryAllDataWithTableName:(NSString *)tableName { +- (NSArray *)queryAllDataInTable:(NSString *)tableName { RLMObjectSchema *objectSchema = [self.realm.schema schemaForClassName:tableName]; RLMResults *results = [self.realm allObjects:tableName]; if (results.count == 0 || !objectSchema) { diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.h b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.h new file mode 100644 index 0000000000..8013e7ee82 --- /dev/null +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.h @@ -0,0 +1,41 @@ +// +// FLEXSQLResult.h +// FLEX +// +// Created by Tanner on 3/3/20. +// Copyright © 2020 Flipboard. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLEXSQLResult : NSObject + +/// Describes the result of a non-select query, or an error of any kind of query ++ (instancetype)message:(NSString *)message; +/// @param rowData A list of rows, where each element in the row +/// corresponds to the column given in /c columnNames ++ (instancetype)columns:(NSArray *)columnNames + rows:(NSArray *> *)rowData; + +@property (nonatomic, readonly, nullable) NSString *message; + +/// A list of column names +@property (nonatomic, readonly, nullable) NSArray *columns; +/// A list of rows, where each element in the row corresponds +/// to the value of the column at the same index in \c columns. +/// +/// That is, given a row, looping over the contents of the row and +/// the contents of \c columns will give you key-value pairs of +/// column names to column values for that row. +@property (nonatomic, readonly, nullable) NSArray *> *rows; +/// A list of rows where the fields are paired to column names. +/// +/// This property is lazily constructed by looping over +/// the rows and columns present in the other two properties. +@property (nonatomic, readonly, nullable) NSArray *> *keyedRows; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.m b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.m new file mode 100644 index 0000000000..3d5e3c4767 --- /dev/null +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLResult.m @@ -0,0 +1,47 @@ +// +// FLEXSQLResult.m +// FLEX +// +// Created by Tanner on 3/3/20. +// Copyright © 2020 Flipboard. All rights reserved. +// + +#import "FLEXSQLResult.h" +#import "NSArray+Functional.h" + +@implementation FLEXSQLResult +@synthesize keyedRows = _keyedRows; + ++ (instancetype)message:(NSString *)message { + return [[self alloc] initWithmessage:message columns:nil rows:nil]; +} + ++ (instancetype)columns:(NSArray *)columnNames rows:(NSArray *> *)rowData { + return [[self alloc] initWithmessage:nil columns:columnNames rows:rowData]; +} + +- (id)initWithmessage:(NSString *)message columns:(NSArray *)columns rows:(NSArray *)rows { + NSParameterAssert(message || (columns && rows)); + NSParameterAssert(columns.count == rows.firstObject.count); + + self = [super init]; + if (self) { + _message = message; + _columns = columns; + _rows = rows; + } + + return self; +} + +- (NSArray *> *)keyedRows { + if (!_keyedRows) { + _keyedRows = [self.rows flex_mapped:^id(NSArray *row, NSUInteger idx) { + return [NSDictionary dictionaryWithObjects:row forKeys:self.columns]; + }]; + } + + return _keyedRows; +} + +@end diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m index 17b33669dc..7b3ef67b5e 100644 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m @@ -9,12 +9,13 @@ #import "FLEXSQLiteDatabaseManager.h" #import "FLEXManager.h" #import "NSArray+Functional.h" +#import "FLEXSQLResult.h" #import -static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; +static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; @interface FLEXSQLiteDatabaseManager () -@property (nonatomic, readonly) sqlite3 *db; +@property (nonatomic) sqlite3 *db; @property (nonatomic, copy) NSString *path; @end @@ -36,7 +37,7 @@ - (instancetype)initWithPath:(NSString *)path { } - (BOOL)open { - if (_db) { + if (self.db) { return YES; } @@ -57,9 +58,9 @@ - (BOOL)open { return YES; } - + - (BOOL)close { - if (!_db) { + if (!self.db) { return YES; } @@ -84,88 +85,85 @@ - (BOOL)close { } } while (retry); - _db = nil; + self.db = nil; return YES; } - (NSArray *)queryAllTables { - return [[self executeQuery:QUERY_TABLENAMES_SQL] flex_mapped:^id(NSArray *table, NSUInteger idx) { + return [[self executeStatement:QUERY_TABLENAMES].rows flex_mapped:^id(NSArray *table, NSUInteger idx) { return table.firstObject; }]; } -- (NSArray *)queryAllColumnsWithTableName:(NSString *)tableName { +- (NSArray *)queryAllColumnsOfTable:(NSString *)tableName { NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName]; - NSArray *results = [self executeQueryWithColumns:sql]; + FLEXSQLResult *results = [self executeStatement:sql]; - return [results flex_mapped:^id(NSDictionary *column, NSUInteger idx) { + return [results.keyedRows flex_mapped:^id(NSDictionary *column, NSUInteger idx) { return column[@"name"]; }]; } -- (NSArray *)queryAllDataWithTableName:(NSString *)tableName { - return [self executeQuery:[@"SELECT * FROM " +- (NSArray *)queryAllDataInTable:(NSString *)tableName { + return [self executeStatement:[@"SELECT * FROM " stringByAppendingString:tableName - ]]; + ]].rows; } -#pragma mark - Private - -/// @return an array of rows, where each row is an array -/// containing the values of each column for that row -- (NSArray *)executeQuery:(NSString *)sql { +- (FLEXSQLResult *)executeStatement:(NSString *)sql { [self open]; - NSMutableArray *results = [NSMutableArray array]; + FLEXSQLResult *result = nil; sqlite3_stmt *pstmt; if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0) == SQLITE_OK) { - while (sqlite3_step(pstmt) == SQLITE_ROW) { - int num_cols = sqlite3_data_count(pstmt); - if (num_cols > 0) { - int columnCount = sqlite3_column_count(pstmt); - - [results addObject:[NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { + NSMutableArray *rows = [NSMutableArray new]; + + // Grab columns + int columnCount = sqlite3_column_count(pstmt); + NSArray *columns = [NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { + return @(sqlite3_column_name(pstmt, (int)i)); + }]; + + // Execute statement + int status; + while ((status = sqlite3_step(pstmt)) == SQLITE_ROW) { + // Grab rows if this is a selection query + int dataCount = sqlite3_data_count(pstmt); + if (dataCount > 0) { + [rows addObject:[NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) { return [self objectForColumnIndex:(int)i stmt:pstmt]; }]]; } } - } - - [self close]; - return results; -} - -/// Like \c executeQuery: except that a list of dictionaries are returned, -/// where the keys are column names and the values are the data. -- (NSArray *)executeQueryWithColumns:(NSString *)sql { - [self open]; - - NSMutableArray *results = [NSMutableArray array]; - - sqlite3_stmt *pstmt; - if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &pstmt, 0) == SQLITE_OK) { - while (sqlite3_step(pstmt) == SQLITE_ROW) { - int num_cols = sqlite3_data_count(pstmt); - if (num_cols > 0) { - int columnCount = sqlite3_column_count(pstmt); - - - NSMutableDictionary *rowFields = [NSMutableDictionary new]; - for (int i = 0; i < columnCount; i++) { - id value = [self objectForColumnIndex:(int)i stmt:pstmt]; - rowFields[@(sqlite3_column_name(pstmt, i))] = value; - } - - [results addObject:rowFields]; + + if (status == SQLITE_DONE) { + if (rows.count) { + // We selected some rows + result = [FLEXSQLResult columns:columns rows:rows]; + } else { + // We executed a query like INSERT, UDPATE, or DELETE + int rowsAffected = sqlite3_changes(_db); + NSString *message = [NSString stringWithFormat:@"%d row(s) affected", rowsAffected]; + result = [FLEXSQLResult message:message]; } + } else { + // An error occured executing the query + result = [FLEXSQLResult message:@(sqlite3_errmsg(_db) ?: "(Execution: empty error)")]; } + } else { + // An error occurred creating the prepared statement + result = [FLEXSQLResult message:@(sqlite3_errmsg(_db) ?: "(Prepared statement: empty error)")]; } + sqlite3_finalize(pstmt); [self close]; - return results; + return result; } + +#pragma mark - Private + - (id)objectForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt*)stmt { int columnType = sqlite3_column_type(stmt, columnIdx); @@ -184,7 +182,7 @@ - (id)objectForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt*)stmt { return [self stringForColumnIndex:columnIdx stmt:stmt] ?: NSNull.null; } } - + - (NSString *)stringForColumnIndex:(int)columnIdx stmt:(sqlite3_stmt *)stmt { if (sqlite3_column_type(stmt, columnIdx) == SQLITE_NULL || columnIdx < 0) { return nil; diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h index d0b5c873b2..077e3fc9f1 100644 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h @@ -10,7 +10,7 @@ @interface FLEXTableContentViewController : UIViewController -@property (nonatomic) NSArray *columns; -@property (nonatomic) NSArray *rows; ++ (instancetype)columns:(NSArray *)columnNames + rows:(NSArray *> *)rowData; @end diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m index 39b0a585ec..2bf5567176 100755 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m @@ -14,21 +14,32 @@ @interface FLEXTableContentViewController () < FLEXMultiColumnTableViewDataSource, FLEXMultiColumnTableViewDelegate > +@property (nonatomic, readonly) NSArray *columns; +@property (nonatomic, copy) NSArray *rows; + @property (nonatomic) FLEXMultiColumnTableView *multiColumnView; @end @implementation FLEXTableContentViewController -- (void)viewDidLoad { - [super viewDidLoad]; - self.edgesForExtendedLayout = UIRectEdgeNone; - ++ (instancetype)columns:(NSArray *)columnNames + rows:(NSArray *> *)rowData { + FLEXTableContentViewController *controller = [self new]; + controller->_columns = columnNames; + controller->_rows = rowData; + return controller; +} + +- (void)loadView { + [super loadView]; [self.view addSubview:self.multiColumnView]; } -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; +- (void)viewDidLoad { + [super viewDidLoad]; + + self.edgesForExtendedLayout = UIRectEdgeNone; [self.multiColumnView reloadData]; } @@ -38,8 +49,8 @@ - (FLEXMultiColumnTableView *)multiColumnView { initWithFrame:FLEXRectSetSize(CGRectZero, self.view.frame.size) ]; - _multiColumnView.dataSource = self; - _multiColumnView.delegate = self; + _multiColumnView.dataSource = self; + _multiColumnView.delegate = self; } return _multiColumnView; diff --git a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.m b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.m index 6a6fcee3cd..db8b930828 100644 --- a/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.m +++ b/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableListViewController.m @@ -12,6 +12,7 @@ #import "FLEXRealmDatabaseManager.h" #import "FLEXTableContentViewController.h" #import "NSArray+Functional.h" +#import "FLEXAlert.h" @interface FLEXTableListViewController () @property (nonatomic, readonly) id dbm; @@ -30,14 +31,53 @@ @implementation FLEXTableListViewController - (instancetype)initWithPath:(NSString *)path { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { - _path = [path copy]; - _dbm = [self databaseManagerForFileAtPath:self.path]; - [self.dbm open]; - [self getAllTables]; + _path = path.copy; + _dbm = [self databaseManagerForFileAtPath:path]; } + return self; } +- (void)viewDidLoad { + [super viewDidLoad]; + + self.showsSearchBar = YES; + [self getAllTables]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemCompose + target:self + action:@selector(queryButtonPressed) + ]; + // Cannot run custom queries on realm databases + self.navigationItem.rightBarButtonItem.enabled = [self.dbm + respondsToSelector:@selector(executeStatement:) + ]; +} + +- (void)queryButtonPressed { + FLEXSQLiteDatabaseManager *database = self.dbm; + + [FLEXAlert makeAlert:^(FLEXAlert *make) { + make.title(@"Execute an SQL query"); + make.textField(nil); + make.button(@"Run").handler(^(NSArray *strings) { + FLEXSQLResult *result = [database executeStatement:strings[0]]; + + if (result.message) { + [FLEXAlert showAlert:@"Message" message:result.message from:self]; + } else { + UIViewController *resultsScreen = [FLEXTableContentViewController + columns:result.columns rows:result.rows + ]; + + [self.navigationController pushViewController:resultsScreen animated:YES]; + } + }); + make.button(@"Cancel").cancelStyle(); + } showFrom:self]; +} + - (id)databaseManagerForFileAtPath:(NSString *)path { NSString *pathExtension = path.pathExtension.lowercaseString; @@ -55,14 +95,9 @@ - (instancetype)initWithPath:(NSString *)path { } - (void)getAllTables { - self.tables = self.filteredTables = [self.dbm queryAllTables];; + self.tables = self.filteredTables = [self.dbm queryAllTables]; } -- (void)viewDidLoad { - [super viewDidLoad]; - - self.showsSearchBar = YES; -} #pragma mark - Search bar @@ -95,15 +130,13 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } - - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - FLEXTableContentViewController *contentViewController = [FLEXTableContentViewController new]; - - contentViewController.rows = [self.dbm queryAllDataWithTableName:self.filteredTables[indexPath.row]]; - contentViewController.columns = [self.dbm queryAllColumnsWithTableName:self.filteredTables[indexPath.row]]; + NSArray *rows = [self.dbm queryAllDataInTable:self.filteredTables[indexPath.row]]; + NSArray *columns = [self.dbm queryAllColumnsOfTable:self.filteredTables[indexPath.row]]; - contentViewController.title = self.filteredTables[indexPath.row]; - [self.navigationController pushViewController:contentViewController animated:YES]; + UIViewController *resultsScreen = [FLEXTableContentViewController columns:columns rows:rows]; + resultsScreen.title = self.filteredTables[indexPath.row]; + [self.navigationController pushViewController:resultsScreen animated:YES]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { diff --git a/FLEX.xcodeproj/project.pbxproj b/FLEX.xcodeproj/project.pbxproj index b7cd427edd..bef10acfc6 100644 --- a/FLEX.xcodeproj/project.pbxproj +++ b/FLEX.xcodeproj/project.pbxproj @@ -245,6 +245,8 @@ C38EF26323A2FCD20047A7EC /* FLEXViewControllerShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */; }; C38F3F31230C958F004E3731 /* FLEXAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = C38F3F2F230C958F004E3731 /* FLEXAlert.h */; }; C38F3F32230C958F004E3731 /* FLEXAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = C38F3F30230C958F004E3731 /* FLEXAlert.m */; }; + C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */ = {isa = PBXBuildFile; fileRef = C397E316240EC98F0091E4EC /* FLEXSQLResult.h */; }; + C397E319240EC98F0091E4EC /* FLEXSQLResult.m in Sources */ = {isa = PBXBuildFile; fileRef = C397E317240EC98F0091E4EC /* FLEXSQLResult.m */; }; C398624D23AD6C67007E6793 /* TBKeyPathSearchController.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624323AD6C67007E6793 /* TBKeyPathSearchController.h */; }; C398624E23AD6C67007E6793 /* TBKeyPath.m in Sources */ = {isa = PBXBuildFile; fileRef = C398624423AD6C67007E6793 /* TBKeyPath.m */; }; C398624F23AD6C67007E6793 /* TBKeyPathTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = C398624523AD6C67007E6793 /* TBKeyPathTokenizer.h */; }; @@ -588,6 +590,8 @@ C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXViewControllerShortcuts.h; sourceTree = ""; }; C38F3F2F230C958F004E3731 /* FLEXAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAlert.h; sourceTree = ""; }; C38F3F30230C958F004E3731 /* FLEXAlert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAlert.m; sourceTree = ""; }; + C397E316240EC98F0091E4EC /* FLEXSQLResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSQLResult.h; sourceTree = ""; }; + C397E317240EC98F0091E4EC /* FLEXSQLResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXSQLResult.m; sourceTree = ""; }; C398624323AD6C67007E6793 /* TBKeyPathSearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBKeyPathSearchController.h; sourceTree = ""; }; C398624423AD6C67007E6793 /* TBKeyPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBKeyPath.m; sourceTree = ""; }; C398624523AD6C67007E6793 /* TBKeyPathTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBKeyPathTokenizer.h; sourceTree = ""; }; @@ -961,6 +965,8 @@ 779B1EBF1C0C4D7C001F5E49 /* DatabaseBrowser */ = { isa = PBXGroup; children = ( + C397E316240EC98F0091E4EC /* FLEXSQLResult.h */, + C397E317240EC98F0091E4EC /* FLEXSQLResult.m */, 222C88211C7339DC007CA15F /* FLEXRealmDefines.h */, 779B1EC01C0C4D7C001F5E49 /* FLEXDatabaseManager.h */, 224D49A41C673AB5000EAB86 /* FLEXRealmDatabaseManager.h */, @@ -1477,6 +1483,7 @@ 224D49A81C673AB5000EAB86 /* FLEXRealmDatabaseManager.h in Headers */, C313853F23F5C1A10046E63C /* FLEXViewControllersViewController.h in Headers */, C398682623AC359600E9E391 /* FLEXShortcutsFactory+Defaults.h in Headers */, + C397E318240EC98F0091E4EC /* FLEXSQLResult.h in Headers */, 7349FD6A22B93CDF00051810 /* FLEXColor.h in Headers */, C36FBFD3230F3B98008D95D5 /* FLEXMethod.h in Headers */, C36FBFD8230F3B98008D95D5 /* FLEXPropertyAttributes.h in Headers */, @@ -1766,6 +1773,7 @@ 3A4C950A1B5B21410088C3F2 /* FLEXFieldEditorView.m in Sources */, 3A4C95061B5B21410088C3F2 /* FLEXArgumentInputViewFactory.m in Sources */, 3A4C95291B5B21410088C3F2 /* FLEXInstancesViewController.m in Sources */, + C397E319240EC98F0091E4EC /* FLEXSQLResult.m in Sources */, C31D93E923E38E97005517BF /* FLEXBlockDescription.m in Sources */, 71E1C2182307FBB800F5032A /* FLEXKeychainTableViewController.m in Sources */, 94A5151E1C4CA1F10063292F /* FLEXExplorerViewController.m in Sources */,