Преглед изворни кода

Add ability to run SQL queries

Les Melnychuk пре 7 година
родитељ
комит
15d7d07809

+ 9 - 2
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h

@@ -12,16 +12,23 @@
 //  which Flying Meat Inc. licenses this file to you.
 
 #import <Foundation/Foundation.h>
+#import "FLEXSQLResult.h"
 
 @protocol FLEXDatabaseManager <NSObject>
 
 @required
+
 + (instancetype)managerForDatabase:(NSString *)path;
 
 - (BOOL)open;
+
 /// @return a list of all table names
 - (NSArray<NSString *> *)queryAllTables;
-- (NSArray<NSString *> *)queryAllColumnsWithTableName:(NSString *)tableName;
-- (NSArray<NSArray *> *)queryAllDataWithTableName:(NSString *)tableName;
+- (NSArray<NSString *> *)queryAllColumnsOfTable:(NSString *)tableName;
+- (NSArray<NSArray *> *)queryAllDataInTable:(NSString *)tableName;
+
+@optional
+
+- (FLEXSQLResult *)executeStatement:(NSString *)SQLStatement;
 
 @end

+ 3 - 2
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXRealmDatabaseManager.m

@@ -8,6 +8,7 @@
 
 #import "FLEXRealmDatabaseManager.h"
 #import "NSArray+Functional.h"
+#import "FLEXSQLResult.h"
 
 #if __has_include(<Realm/Realm.h>)
 #import <Realm/Realm.h>
@@ -68,7 +69,7 @@ static Class RLMRealmClass = nil;
     }];
 }
 
-- (NSArray<NSString *> *)queryAllColumnsWithTableName:(NSString *)tableName {
+- (NSArray<NSString *> *)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 @@ static Class RLMRealmClass = nil;
     }];
 }
 
-- (NSArray<NSArray *> *)queryAllDataWithTableName:(NSString *)tableName {
+- (NSArray<NSArray *> *)queryAllDataInTable:(NSString *)tableName {
     RLMObjectSchema *objectSchema = [self.realm.schema schemaForClassName:tableName];
     RLMResults *results = [self.realm allObjects:tableName];
     if (results.count == 0 || !objectSchema) {

+ 41 - 0
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 <Foundation/Foundation.h>
+
+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<NSString *> *)columnNames
+                rows:(NSArray<NSArray<NSString *> *> *)rowData;
+
+@property (nonatomic, readonly, nullable) NSString *message;
+
+/// A list of column names
+@property (nonatomic, readonly, nullable) NSArray<NSString *> *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<NSArray<NSString *> *> *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<NSDictionary<NSString *, id> *> *keyedRows;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 47 - 0
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<NSString *> *)columnNames rows:(NSArray<NSArray<NSString *> *> *)rowData {
+    return [[self alloc] initWithmessage:nil columns:columnNames rows:rowData];
+}
+
+- (id)initWithmessage:(NSString *)message columns:(NSArray *)columns rows:(NSArray<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<NSDictionary<NSString *,id> *> *)keyedRows {
+    if (!_keyedRows) {
+        _keyedRows = [self.rows flex_mapped:^id(NSArray<NSString *> *row, NSUInteger idx) {
+            return [NSDictionary dictionaryWithObjects:row forKeys:self.columns];
+        }];
+    }
+    
+    return _keyedRows;
+}
+
+@end

+ 53 - 55
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXSQLiteDatabaseManager.m

@@ -9,12 +9,13 @@
 #import "FLEXSQLiteDatabaseManager.h"
 #import "FLEXManager.h"
 #import "NSArray+Functional.h"
+#import "FLEXSQLResult.h"
 #import <sqlite3.h>
 
-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 @@ static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master
 }
 
 - (BOOL)open {
-    if (_db) {
+    if (self.db) {
         return YES;
     }
     
@@ -57,9 +58,9 @@ static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master
     
     return YES;
 }
-
+    
 - (BOOL)close {
-    if (!_db) {
+    if (!self.db) {
         return YES;
     }
     
@@ -84,88 +85,85 @@ static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master
         }
     } while (retry);
     
-    _db = nil;
+    self.db = nil;
     return YES;
 }
 
 - (NSArray<NSString *> *)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<NSString *> *)queryAllColumnsWithTableName:(NSString *)tableName {
+- (NSArray<NSString *> *)queryAllColumnsOfTable:(NSString *)tableName {
     NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName];
-    NSArray<NSDictionary *> *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<NSArray *> *)queryAllDataWithTableName:(NSString *)tableName {
-    return [self executeQuery:[@"SELECT * FROM "
+- (NSArray<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<NSArray *> *)executeQuery:(NSString *)sql {
+- (FLEXSQLResult *)executeStatement:(NSString *)sql {
     [self open];
     
-    NSMutableArray<NSArray *> *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<NSArray *> *rows = [NSMutableArray new];
+        
+        // Grab columns
+        int columnCount = sqlite3_column_count(pstmt);
+        NSArray<NSString *> *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<NSDictionary *> *)executeQueryWithColumns:(NSString *)sql {
-    [self open];
-    
-    NSMutableArray<NSDictionary *> *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 @@ static NSString * const QUERY_TABLENAMES_SQL = @"SELECT name FROM sqlite_master
             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;

+ 2 - 2
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.h

@@ -10,7 +10,7 @@
 
 @interface FLEXTableContentViewController : UIViewController
 
-@property (nonatomic) NSArray<NSString *> *columns;
-@property (nonatomic) NSArray<NSArray *> *rows;
++ (instancetype)columns:(NSArray<NSString *> *)columnNames
+                   rows:(NSArray<NSArray<NSString *> *> *)rowData;
 
 @end

+ 19 - 8
Classes/GlobalStateExplorers/DatabaseBrowser/FLEXTableContentViewController.m

@@ -14,21 +14,32 @@
 @interface FLEXTableContentViewController () <
     FLEXMultiColumnTableViewDataSource, FLEXMultiColumnTableViewDelegate
 >
+@property (nonatomic, readonly) NSArray<NSString *> *columns;
+@property (nonatomic, copy) NSArray<NSArray *> *rows;
+
 @property (nonatomic) FLEXMultiColumnTableView *multiColumnView;
 @end
 
 @implementation FLEXTableContentViewController
 
-- (void)viewDidLoad {
-    [super viewDidLoad];
-    self.edgesForExtendedLayout = UIRectEdgeNone;
-    
++ (instancetype)columns:(NSArray<NSString *> *)columnNames
+                   rows:(NSArray<NSArray<NSString *> *> *)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 @@
             initWithFrame:FLEXRectSetSize(CGRectZero, self.view.frame.size)
         ];
         
-        _multiColumnView.dataSource       = self;
-        _multiColumnView.delegate         = self;
+        _multiColumnView.dataSource = self;
+        _multiColumnView.delegate   = self;
     }
     
     return _multiColumnView;

+ 50 - 17
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<FLEXDatabaseManager> dbm;
@@ -30,14 +31,53 @@
 - (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<NSString *> *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<FLEXDatabaseManager>)databaseManagerForFileAtPath:(NSString *)path {
     NSString *pathExtension = path.pathExtension.lowercaseString;
     
@@ -55,14 +95,9 @@
 }
 
 - (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 @@
     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 {

+ 8 - 0
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 = "<group>"; };
 		C38F3F2F230C958F004E3731 /* FLEXAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXAlert.h; sourceTree = "<group>"; };
 		C38F3F30230C958F004E3731 /* FLEXAlert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXAlert.m; sourceTree = "<group>"; };
+		C397E316240EC98F0091E4EC /* FLEXSQLResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXSQLResult.h; sourceTree = "<group>"; };
+		C397E317240EC98F0091E4EC /* FLEXSQLResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXSQLResult.m; sourceTree = "<group>"; };
 		C398624323AD6C67007E6793 /* TBKeyPathSearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBKeyPathSearchController.h; sourceTree = "<group>"; };
 		C398624423AD6C67007E6793 /* TBKeyPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBKeyPath.m; sourceTree = "<group>"; };
 		C398624523AD6C67007E6793 /* TBKeyPathTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBKeyPathTokenizer.h; sourceTree = "<group>"; };
@@ -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 */,