From 7f66ab71ca1cdd2bea637cc546837c28fbdd78ab Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Mon, 17 Aug 2009 00:10:02 +0000 Subject: Rework SQL export: - Added an MCPStreamingResult class to MCPKit, to allow streaming results from the server including fast array access of each row - Tweak SQL export to use the streaming result class and to keep memory usage lower End result is generally faster exports, more accurate progress bars, and much much lower (and consistent) memory usage. --- Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h | 8 +- Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m | 68 +++++-- Frameworks/MCPKit/MCPFoundationKit/MCPKit.h | 1 + .../MCPKit/MCPFoundationKit/MCPStreamingResult.h | 41 +++++ .../MCPKit/MCPFoundationKit/MCPStreamingResult.m | 203 +++++++++++++++++++++ Source/TableDump.m | 42 +++-- sequel-pro.xcodeproj/project.pbxproj | 12 +- 7 files changed, 344 insertions(+), 31 deletions(-) create mode 100644 Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h create mode 100644 Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h index 9639a742..932237d8 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h @@ -32,7 +32,7 @@ #import "mysql.h" -@class MCPResult; +@class MCPResult, MCPStreamingResult; @protocol MCPConnectionProxy; /** @@ -185,11 +185,15 @@ static inline NSData* NSStringDataUsingLossyEncoding(NSString* self, int encodin - (NSString *)prepareString:(NSString *)theString; - (NSString *)quoteObject:(id)theObject; - (MCPResult *)queryString:(NSString *)query; -- (MCPResult *)queryString:(NSString *)query usingEncoding:(NSStringEncoding)encoding; +- (MCPStreamingResult *)streamingQueryString:(NSString *)query; +- (id)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding streamingResult:(BOOL) streamResult; - (double)lastQueryExecutionTime; - (my_ulonglong)affectedRows; - (my_ulonglong)insertId; +// Locking +- (void)unlockConnection; + // Database structure - (MCPResult *)listDBs; - (MCPResult *)listDBsLike:(NSString *)dbsName; diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m index 8ed2884b..b311ac14 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m @@ -28,6 +28,7 @@ #import "MCPConnection.h" #import "MCPResult.h" +#import "MCPStreamingResult.h" #import "MCPNumber.h" #import "MCPNull.h" #import "MCPConnectionProxy.h" @@ -1230,15 +1231,26 @@ static void forcePingTimeout(int signalNumber) */ - (MCPResult *)queryString:(NSString *)query { - return [self queryString:query usingEncoding:mEncoding]; + return [self queryString:query usingEncoding:mEncoding streamingResult:NO]; +} + +/** + * Takes a query string and returns an MCPStreamingResult representing the result of the query. + * The returned MCPStreamingResult is retained and the client is responsible for releasing it. + * If no fields are present in the result, nil will be returned. + */ +- (MCPStreamingResult *)streamingQueryString:(NSString *)query +{ + return [self queryString:query usingEncoding:mEncoding streamingResult:YES]; } /** * Error checks connection extensively - if this method fails due to a connection error, it will ask how to * proceed and loop depending on the status, not returning control until either the query has been executed * and the result can be returned or the connection and document have been closed. + * If using streamingResult, the caller is responsible for releasing the result set. */ -- (MCPResult *)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding +- (id)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding streamingResult:(BOOL) streamResult { MCPResult *theResult = nil; double queryStartTime, queryExecutionTime; @@ -1340,9 +1352,16 @@ static void forcePingTimeout(int signalNumber) if (0 == queryResultCode) { if (mysql_field_count(mConnection) != 0) { - theResult = [[MCPResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone]; - - [queryLock unlock]; + + // For normal result sets, fetch the results and unlock the connection + if (!streamResult) { + theResult = [[MCPResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone]; + [queryLock unlock]; + + // For streaming result sets, fetch the result pointer and leave the connection locked + } else { + theResult = [[MCPStreamingResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone connection:self]; + } // Ensure no problem occurred during the result fetch if (mysql_errno(mConnection) != 0) { @@ -1356,9 +1375,13 @@ static void forcePingTimeout(int signalNumber) queryErrorMessage = [[NSString alloc] initWithString:@""]; queryErrorId = 0; - queryAffectedRows = mysql_affected_rows(mConnection); + if (!streamResult) { + queryAffectedRows = mysql_affected_rows(mConnection); + } else { + queryAffectedRows = 0; + } - // On failure, set the error messages and IDs + // On failure, set the error messages and IDs } else { [queryLock unlock]; @@ -1375,13 +1398,16 @@ static void forcePingTimeout(int signalNumber) break; } - // If the mysql thread id has changed as a result of a connection error, - // ensure connection details are still correct - if (connectionThreadId != mConnection->thread_id) [self restoreConnectionDetails]; - - // If max_allowed_packet was changed, reset it to default - if(currentMaxAllowedPacket > -1) - [self setMaxAllowedPacketTo:currentMaxAllowedPacket resetSize:YES]; + if (!streamResult) { + + // If the mysql thread id has changed as a result of a connection error, + // ensure connection details are still correct + if (connectionThreadId != mConnection->thread_id) [self restoreConnectionDetails]; + + // If max_allowed_packet was changed, reset it to default + if(currentMaxAllowedPacket > -1) + [self setMaxAllowedPacketTo:currentMaxAllowedPacket resetSize:YES]; + } // Update error strings and IDs lastQueryErrorId = queryErrorId; @@ -1397,6 +1423,7 @@ static void forcePingTimeout(int signalNumber) (void)(*startKeepAliveTimerResettingStatePtr)(self, startKeepAliveTimerResettingStateSEL, YES); if (!theResult) return nil; + if (streamResult) return theResult; return [theResult autorelease]; } @@ -1434,6 +1461,17 @@ static void forcePingTimeout(int signalNumber) return 0; } +#pragma mark - +#pragma mark Connection locking + +/** + * Unlock the connection + */ +- (void)unlockConnection +{ + [queryLock unlock]; +} + #pragma mark - #pragma mark Database structure @@ -1844,7 +1882,7 @@ static void forcePingTimeout(int signalNumber) - (int)getMaxAllowedPacket { MCPResult *r; - r = [self queryString:@"SELECT @@global.max_allowed_packet" usingEncoding:mEncoding]; + r = [self queryString:@"SELECT @@global.max_allowed_packet" usingEncoding:mEncoding streamingResult:NO]; if (![[self getLastErrorMessage] isEqualToString:@""]) { if ([self isConnected]) NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving max_allowed_packet size:\n\n%@", [self getLastErrorMessage]], @"OK", nil, nil); diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h b/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h index 549e4144..11d2c72e 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPKit.h @@ -31,6 +31,7 @@ #import #import #import +#import #import #import #import diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h new file mode 100644 index 00000000..168f85d3 --- /dev/null +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h @@ -0,0 +1,41 @@ +// +// $Id$ +// +// MCPStreamingResult.h +// sequel-pro +// +// Created by Rowan Beentje on Aug 16, 2009 +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import +#import "MCPResult.h" + +@class MCPConnection; + +@interface MCPStreamingResult : MCPResult +{ + MCPConnection *parentConnection; + + MYSQL_FIELD *fieldDefinitions; +} + +- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection; +- (NSArray *)fetchNextRowAsArray; + +@end diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m new file mode 100644 index 00000000..0b46ddc5 --- /dev/null +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m @@ -0,0 +1,203 @@ +// +// $Id$ +// +// MCPStreamingResult.m +// sequel-pro +// +// Created by Rowan Beentje on Aug 16, 2009 +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "MCPStreamingResult.h" +#import "MCPConnection.h" +#import "MCPNull.h" +#import "MCPNumber.h" + +@implementation MCPStreamingResult : MCPResult + +#pragma mark - +#pragma mark Setup and teardown + +/** + * Initialise a MCPStreamingResult in the same way as MCPResult - as used + * internally by the MCPConnection !{queryString:} method. + */ +- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection +{ + if ((self = [super init])) { + mEncoding = theEncoding; + mTimeZone = [theTimeZone retain]; + parentConnection = theConnection; + + if (mResult) { + mysql_free_result(mResult); + mResult = NULL; + } + + if (mNames) { + [mNames release]; + mNames = NULL; + } + + mResult = mysql_use_result(mySQLPtr); + + if (mResult) { + mNumOfFields = mysql_num_fields(mResult); + fieldDefinitions = mysql_fetch_fields(mResult); + } else { + mNumOfFields = 0; + } + + if (mMySQLLocales == NULL) { + mMySQLLocales = [[MCPConnection getMySQLLocales] retain]; + } + } + + return self; +} + +/** + * Deallocate the result and unlock the parent connection for further use + */ +- (void) dealloc +{ + [parentConnection unlockConnection]; + + [super dealloc]; +} + +#pragma mark - +#pragma mark Results fetching + +/** + * Retrieve the next row of the result as an array. Should be called in a loop + * until nil is returned to ensure all the results have been retrieved. + */ +- (NSArray *)fetchNextRowAsArray +{ + MYSQL_ROW theRow; + unsigned long *fieldLengths; + int i; + NSMutableArray *returnArray; + + // Retrieve the next row + theRow = mysql_fetch_row(mResult); + + // If no data was returned, we're at the end of the result set - return nil. + if (theRow == NULL) return nil; + + // Retrieve the lengths of the returned data + fieldLengths = mysql_fetch_lengths(mResult); + + // Initialise the array to return + returnArray = [NSMutableArray arrayWithCapacity:mNumOfFields]; + for (i = 0; i < mNumOfFields; i++) { + id cellData; + + // Use NSNulls for the NULL data type + if (theRow[i] == NULL) { + cellData = [NSNull null]; + + // Otherwise, switch by data type + } else { + + // Create a null-terminated data string for processing + char *theData = calloc(sizeof(char), fieldLengths[i]+1); + memcpy(theData, theRow[i], fieldLengths[i]); + theData[fieldLengths[i]] = '\0'; + + switch (fieldDefinitions[i].type) { + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONG: + case FIELD_TYPE_LONGLONG: + case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_NEWDECIMAL: + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_TIMESTAMP: + case FIELD_TYPE_DATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_STRING: + case FIELD_TYPE_SET: + case FIELD_TYPE_ENUM: + case FIELD_TYPE_NEWDATE: // Don't know what the format for this type is... + cellData = [NSString stringWithCString:theData encoding:mEncoding]; + break; + + case FIELD_TYPE_BIT: + cellData = [NSString stringWithFormat:@"%u", theData[0]]; + break; + + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_LONG_BLOB: + + // For binary data, return the data + if (fieldDefinitions[i].flags & BINARY_FLAG) { + cellData = [NSData dataWithBytes:theData length:fieldLengths[i]]; + + // For string data, convert to text + } else { + cellData = [[NSString alloc] initWithBytes:theData length:fieldLengths[i] encoding:mEncoding]; + if (cellData) [cellData autorelease]; + } + break; + + case FIELD_TYPE_NULL: + cellData = [NSNull null]; + break; + + default: + NSLog(@"in fetchNextRowAsArray : Unknown type : %d for column %d, sending back a NSData object", (int)fieldDefinitions[i].type, (int)i); + cellData = [NSData dataWithBytes:theData length:fieldLengths[i]]; + break; + } + + free(theData); + + // If a creator returned a nil object, replace with NSNull + if (cellData == nil) cellData = [NSNull null]; + } + + [returnArray insertObject:cellData atIndex:i]; + } + + return returnArray; +} + +#pragma mark - +#pragma mark Overrides for safety + +- (my_ulonglong) numOfRows +{ + NSLog(@"numOfRows cannot be used with streaming results"); + return 0; +} + +- (void) dataSeek:(my_ulonglong) row +{ + NSLog(@"dataSeek cannot be used with streaming results"); +} + +@end \ No newline at end of file diff --git a/Source/TableDump.m b/Source/TableDump.m index ac4cd666..e027230c 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -501,7 +501,7 @@ NSString *q = [NSArrayObjectAtIndex(queries, i) stringByTrimmingCharactersInSet:whitespaceAndNewline]; if (![q length]) continue; - [mySQLConnection queryString:q usingEncoding:NSUTF8StringEncoding]; + [mySQLConnection queryString:q usingEncoding:NSUTF8StringEncoding streamingResult:NO]; if ([[mySQLConnection getLastErrorMessage] length] && ![[mySQLConnection getLastErrorMessage] isEqualToString:@"Query was empty"]) { [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), (i+1),[mySQLConnection getLastErrorMessage]]]; @@ -860,6 +860,8 @@ // int progressBarWidth; int tableType = SP_TABLETYPE_TABLE; //real tableType will be setup later MCPResult *queryResult; + MCPStreamingResult *streamingResult; + NSAutoreleasePool *exportAutoReleasePool = nil; NSString *tableName, *tableColumnTypeGrouping, *previousConnectionEncoding; NSArray *fieldNames; NSArray *theRow; @@ -867,7 +869,7 @@ NSMutableDictionary *viewSyntaxes = [NSMutableDictionary dictionary]; NSMutableString *metaString = [NSMutableString string]; NSMutableString *cellValue = [NSMutableString string]; - NSMutableString *sqlString = [NSMutableString string]; + NSMutableString *sqlString = [[NSMutableString alloc] init]; NSMutableString *errors = [NSMutableString string]; NSDictionary *tableDetails; NSMutableArray *tableColumnNumericStatus; @@ -988,10 +990,7 @@ // Add the table content if required if ( [addTableContentSwitch state] == NSOnState && tableType == SP_TABLETYPE_TABLE ) { - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]]]; - fieldNames = [queryResult fetchFieldNames]; - rowCount = [queryResult numOfRows]; - + // Retrieve the table details via the data class, and use it to build an array containing column numeric status tableDetails = [NSDictionary dictionaryWithDictionary:[tableDataInstance informationForTable:tableName]]; colCount = [[tableDetails objectForKey:@"columns"] count]; @@ -1005,6 +1004,13 @@ [tableColumnNumericStatus addObject:[NSNumber numberWithBool:NO]]; } } + + // Retrieve the number of rows in the table for progress bar drawing + rowCount = [[[[mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [tableName backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] intValue]; + + // Set up a result set in streaming mode + streamingResult = [mySQLConnection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]]]; + fieldNames = [queryResult fetchFieldNames]; // Update the progress text and set the progress bar back to determinate [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %i of %i (%@): Dumping...", @"text showing that app is writing data for table dump"), (i+1), [selectedTables count], tableName]]; @@ -1015,7 +1021,6 @@ [singleProgressBar displayIfNeeded]; if (rowCount) { - [queryResult dataSeek:0]; queryLength = 0; // Lock the table for writing and disable keys if supported @@ -1029,12 +1034,14 @@ [tableName backtickQuotedString], [fieldNames componentsJoinedAndBacktickQuoted]] dataUsingEncoding:NSUTF8StringEncoding]]; // Iterate through the rows to construct a VALUES group for each - for ( j = 0 ; j < rowCount ; j++ ) { - theRow = [queryResult fetchRowAsArray]; + j = 0; + exportAutoReleasePool = [[NSAutoreleasePool alloc] init]; + while (theRow = [streamingResult fetchNextRowAsArray]) { + j++; [sqlString setString:@""]; // Update the progress bar - [singleProgressBar setDoubleValue:((j+1)*100/rowCount)]; + [singleProgressBar setDoubleValue:(j*100/rowCount)]; if ((int)[singleProgressBar doubleValue] > lastProgressValue) { lastProgressValue = (int)[singleProgressBar doubleValue]; [singleProgressBar displayIfNeeded]; @@ -1046,7 +1053,7 @@ if ( [[theRow objectAtIndex:t] isMemberOfClass:[NSNull class]] ) { [sqlString appendString:@"NULL"]; - // Add data types directly as hex data + // Add data types directly as hex data } else if ( [[theRow objectAtIndex:t] isKindOfClass:[NSData class]] ) { [sqlString appendString:@"X'"]; [sqlString appendString:[mySQLConnection prepareBinaryData:[theRow objectAtIndex:t]]]; @@ -1088,6 +1095,10 @@ [sqlString appendString:[NSString stringWithFormat:@");\n\nINSERT INTO %@ (%@)\nVALUES\n\t(", [tableName backtickQuotedString], [fieldNames componentsJoinedAndBacktickQuoted]]]; queryLength = 0; + + // Use the opportunity to drain and reset the autorelease pool + [exportAutoReleasePool drain]; + exportAutoReleasePool = [[NSAutoreleasePool alloc] init]; } else { [sqlString appendString:@"),\n\t("]; } @@ -1115,7 +1126,13 @@ dataUsingEncoding:NSUTF8StringEncoding]]; } } + + // Drain the autorelease pool + [exportAutoReleasePool drain]; } + + // Release the result set + [streamingResult release]; } // Add an additional separator between tables @@ -1167,7 +1184,8 @@ [NSApp endSheet:errorsSheet]; [errorsSheet orderOut:nil]; } - + + [sqlString release]; return TRUE; } diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 06ce19ec..c3ab1a7d 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -56,7 +56,6 @@ 17B7B5DD101603B200F057DE /* MCPNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 17B7B5CE101603B200F057DE /* MCPNumber.m */; }; 17B7B5DE101603B200F057DE /* MCPResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5CF101603B200F057DE /* MCPResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17B7B5DF101603B200F057DE /* MCPResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 17B7B5D0101603B200F057DE /* MCPResult.m */; }; - 17B7B5E0101603B200F057DE /* MCPResultPlus.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5D1101603B200F057DE /* MCPResultPlus.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17B7B5E1101603B200F057DE /* MCPResultPlus.m in Sources */ = {isa = PBXBuildFile; fileRef = 17B7B5D2101603B200F057DE /* MCPResultPlus.m */; }; 17B7B5EA101603D200F057DE /* my_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5E2101603D200F057DE /* my_alloc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17B7B5EB101603D200F057DE /* my_list.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5E3101603D200F057DE /* my_list.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -136,6 +135,7 @@ 58186D210F4CB38900851FE9 /* ConnectionErrorDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */; }; 5822C9B51000DB2400DCC3D6 /* SPConnectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5822C9B41000DB2400DCC3D6 /* SPConnectionController.m */; }; 5822CAE110011C8000DCC3D6 /* ConnectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5822CADF10011C8000DCC3D6 /* ConnectionView.xib */; }; + 583B77D4103870C800B21F7E /* MCPStreamingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 583B779810386B0200B21F7E /* MCPStreamingResult.m */; }; 5841423F0F97E11000A34B47 /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */; }; 584192A1101E57BB0089807F /* NSMutableArray-MultipleSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 584192A0101E57BB0089807F /* NSMutableArray-MultipleSort.m */; }; 584F5F8F0F50ACD800036517 /* table-view-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584F5F8E0F50ACD800036517 /* table-view-small.tiff */; }; @@ -146,6 +146,8 @@ 588B2CC90FE5641E00EC5FC0 /* ssh-connecting.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC60FE5641E00EC5FC0 /* ssh-connecting.png */; }; 588B2CCA0FE5641E00EC5FC0 /* ssh-disconnected.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC70FE5641E00EC5FC0 /* ssh-disconnected.png */; }; 589235321020C1230011DE00 /* SPHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 589235301020C1230011DE00 /* SPHistoryController.m */; }; + 58BC5E56103898140058C2E6 /* MCPStreamingResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 583B779710386B0200B21F7E /* MCPStreamingResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58BC5E571038983E0058C2E6 /* MCPResultPlus.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5D1101603B200F057DE /* MCPResultPlus.h */; settings = {ATTRIBUTES = (Public, ); }; }; 58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C56EF40F438E120035701E /* SPDataCellFormatter.m */; }; 58CB20ED0F79A75D005EA204 /* button_edit_mode_selected.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 58CB20EC0F79A75D005EA204 /* button_edit_mode_selected.tiff */; }; 58CDB3300FCE138D00F8ACA3 /* SPSSHTunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 58CDB32F0FCE138D00F8ACA3 /* SPSSHTunnel.m */; }; @@ -491,6 +493,8 @@ 5822C9B31000DB2400DCC3D6 /* SPConnectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConnectionController.h; sourceTree = ""; }; 5822C9B41000DB2400DCC3D6 /* SPConnectionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConnectionController.m; sourceTree = ""; }; 5822CAE010011C8000DCC3D6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/ConnectionView.xib; sourceTree = ""; }; + 583B779710386B0200B21F7E /* MCPStreamingResult.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = MCPStreamingResult.h; sourceTree = ""; }; + 583B779810386B0200B21F7E /* MCPStreamingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCPStreamingResult.m; sourceTree = ""; }; 5841423D0F97E11000A34B47 /* NoodleLineNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberView.h; sourceTree = ""; }; 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleLineNumberView.m; sourceTree = ""; }; 5841929F101E57BB0089807F /* NSMutableArray-MultipleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray-MultipleSort.h"; sourceTree = ""; }; @@ -793,6 +797,8 @@ 17B7B5D0101603B200F057DE /* MCPResult.m */, 17B7B5D1101603B200F057DE /* MCPResultPlus.h */, 17B7B5D2101603B200F057DE /* MCPResultPlus.m */, + 583B779710386B0200B21F7E /* MCPStreamingResult.h */, + 583B779810386B0200B21F7E /* MCPStreamingResult.m */, ); path = MCPFoundationKit; sourceTree = ""; @@ -1244,7 +1250,8 @@ 17B7B5DA101603B200F057DE /* MCPNull.h in Headers */, 17B7B5DC101603B200F057DE /* MCPNumber.h in Headers */, 17B7B5DE101603B200F057DE /* MCPResult.h in Headers */, - 17B7B5E0101603B200F057DE /* MCPResultPlus.h in Headers */, + 58BC5E571038983E0058C2E6 /* MCPResultPlus.h in Headers */, + 58BC5E56103898140058C2E6 /* MCPStreamingResult.h in Headers */, 17B7B5EA101603D200F057DE /* my_alloc.h in Headers */, 17B7B5EB101603D200F057DE /* my_list.h in Headers */, 17B7B5EC101603D200F057DE /* mysql.h in Headers */, @@ -1536,6 +1543,7 @@ 17B7B5DD101603B200F057DE /* MCPNumber.m in Sources */, 17B7B5DF101603B200F057DE /* MCPResult.m in Sources */, 17B7B5E1101603B200F057DE /* MCPResultPlus.m in Sources */, + 583B77D4103870C800B21F7E /* MCPStreamingResult.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- cgit v1.2.3