Skip to content

Commit

Permalink
Further work on implementing Custom Query Result History
Browse files Browse the repository at this point in the history
• Now supports multiple results (from stored procedure calls)
• UI still messy and error messages are no longer displayed :(
  • Loading branch information
jakob committed Jul 1, 2010
1 parent 69bc48a commit 7f1b482
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

@protocol MCPConnectionProxy;

@class MCPResult, MCPStreamingResult;
@class MCPResult, MCPStreamingResult, MCPMultiResult;

@interface NSObject (MCPConnectionDelegate)

Expand Down Expand Up @@ -271,6 +271,7 @@ void performThreadedKeepAlive(void *ptr);
- (MCPResult *)queryString:(NSString *)query;
- (MCPStreamingResult *)streamingQueryString:(NSString *)query;
- (MCPStreamingResult *)streamingQueryString:(NSString *)query useLowMemoryBlockingStreaming:(BOOL)fullStream;
- (MCPMultiResult *)streamingMultiQueryString:(NSString *)query;
- (id)queryString:(NSString *)query usingEncoding:(NSStringEncoding)encoding streamingResult:(NSInteger)streamResult;
- (my_ulonglong)affectedRows;
- (my_ulonglong)insertId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#import "MCPNumber.h"
#import "MCPNull.h"
#import "MCPStreamingResult.h"
#import "MCPMultiResult.h"
#import "MCPConnectionProxy.h"
#import "MCPStringAdditions.h"
#import "SPStringAdditions.h"
Expand Down Expand Up @@ -1362,6 +1363,16 @@ - (MCPStreamingResult *)streamingQueryString:(NSString *)query useLowMemoryBlock
return [self queryString:query usingEncoding:mEncoding streamingResult:(fullStream?MCPStreamingLowMem:MCPStreamingFast)];
}

/**
* Takes a query string and returns an MCPMultiResult representing the (possible multiple) results of the query.
* Just use [MCPMultiResult nextResult] to get MCPStreamingresults, until nil is returned.
*/
- (MCPMultiResult *)streamingMultiQueryString:(NSString *)query
{
return [self queryString:query usingEncoding:mEncoding streamingResult:MCPStreamingMulti];
}


/**
* 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
Expand Down Expand Up @@ -1494,6 +1505,8 @@ - (id)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding s
theResult = [[MCPStreamingResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone connection:self withFullStreaming:NO];
} else if (streamResultType == MCPStreamingLowMem) {
theResult = [[MCPStreamingResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone connection:self withFullStreaming:YES];
} else if (streamResultType == MCPStreamingMulti) {
theResult = [[MCPMultiResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone connection:self];
}

// Ensure no problem occurred during the result fetch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ enum
{
MCPStreamingNone = 0,
MCPStreamingFast = 1,
MCPStreamingLowMem = 2
MCPStreamingLowMem = 2,
MCPStreamingMulti = 3
};
typedef NSUInteger MCPQueryStreamingType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#import <MCPKit/MCPNull.h>
#import <MCPKit/MCPResult.h>
#import <MCPKit/MCPStreamingResult.h>
#import <MCPKit/MCPMultiResult.h>
#import <MCPKit/MCPConnection.h>
#import <MCPKit/MCPNumber.h>
#import <MCPKit/MCPResultPlus.h>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// MCPMultiResult.h
// sequel-pro
//
// Created by Jakob on 01.07.10.
//

#import <Foundation/Foundation.h>

#import "mysql.h"

@class MCPConnection;
@class MCPStreamingResult;

@interface MCPMultiResult : NSObject {
NSStringEncoding *mEncoding;
NSTimeZone *mTimeZone;
MYSQL *mConnection;
MCPConnection *parentConnection;


NSMutableArray *results;
BOOL finishedCreatingResults;
NSConditionLock *pseudoConnectionLock;
}
- (id)initWithMySQLPtr:(MYSQL *)aConnection encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection;
- (void) resultFetcherTask;
- (void) unlockConnection;
- (BOOL) isConnected;
- (void) updateErrorStatuses;
- (void) dealloc;
- (NSDictionary*) nextResultSet;

@end
143 changes: 143 additions & 0 deletions multiple-results/Frameworks/MCPKit/MCPFoundationKit/MCPMultiResult.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//
// MCPMultiResult.m
// sequel-pro
//
// Created by Jakob on 01.07.10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "MCPMultiResult.h"
#import "MCPStreamingResult.h"
#import "MCPConstants.h"


/**
* MCMultiResult is used by [MCPConnection streamingMultiQueryString:] to support queries that
* allow multiple results. This is only relevant when using call -- otherwise a query can never
* have more than one result.
*
* MCPMultiResult works by creating MCPStreamingResults for every result of the query. It always
* uses cached streaming mode, so it can immediately continue with the next result as soon as the
* first is ready.
*
* If a second result is requested before the first has finished downloading, execution is blocked
* until the download is finished. We have to wait, because otherwise we don't know if there are
* further results or not.
*
* Usage: just use [MCPConnection streamingMultiQueryString:] and then call [MCPMultiResult nextResult]
* until nil is returned, signalling that there are no more results.
*/
@implementation MCPMultiResult


/**
* Initialise a MCPMultiResult in the same way as MCPResult - as used
* internally by the MCPConnection !{queryString:} method.
*/
- (id)initWithMySQLPtr:(MYSQL *)aConnection encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection
{
if (!(self = [super init])) return nil;
mEncoding = theEncoding;
mTimeZone = [theTimeZone retain];
mConnection = aConnection;

parentConnection = theConnection;

results = [[NSMutableArray alloc] initWithCapacity:10];
pseudoConnectionLock = [[NSConditionLock alloc] initWithCondition:MCPConnectionBusy];

//[NSThread detachNewThreadSelector:@selector(resultFetcherTask) toTarget:self withObject:nil];

NSThread *resultFetcherThread = [[NSThread alloc] initWithTarget:self selector:@selector(resultFetcherTask) object:nil];
[resultFetcherThread setName:@"MCPMultiResult Result Fetcher thread"];
[resultFetcherThread start];

return self;
}

- (void) resultFetcherTask
{
[[NSThread currentThread] setName:@"MCPMultiResult Result Fetcher Thread"];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
do
{
NSMutableDictionary *nextResult = [NSMutableDictionary dictionaryWithCapacity:10];
[nextResult setObject:[NSNumber numberWithInt:mysql_affected_rows(mConnection)] forKey:@"affected_rows"];
[nextResult setObject:[NSNumber numberWithInt:mysql_field_count(mConnection)] forKey:@"field_count"];
[nextResult setObject:[NSNumber numberWithInt:mysql_errno(mConnection)] forKey:@"errno"];
[nextResult setObject:[parentConnection stringWithCString:mysql_error(mConnection)] forKey:@"error"];
if (mysql_field_count(mConnection)) {
[nextResult setObject:[[[MCPStreamingResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone connection:self] autorelease] forKey:@"result"];
} else {
[self unlockConnection];
}
@synchronized(results){
[results addObject:nextResult];
}
NSLog(@"Waiting for result to be processed.");
[pseudoConnectionLock lockWhenCondition:MCPConnectionIdle];
NSLog(@"Result processed.");
[pseudoConnectionLock unlockWithCondition:MCPConnectionBusy];
} while(!mysql_next_result(mConnection));
[parentConnection unlockConnection];
finishedCreatingResults = YES;
NSLog(@"All results processed.");
[pool release];
}

- (void) unlockConnection
{
[pseudoConnectionLock lock];
[pseudoConnectionLock unlockWithCondition:MCPConnectionIdle];
}

- (BOOL) isConnected
{
return [parentConnection isConnected];
}

- (void) updateErrorStatuses
{
[parentConnection updateErrorStatuses];
}

- (void) dealloc
{
// remove all results
[results release];

[pseudoConnectionLock release];
[mTimeZone release];
}

- (NSDictionary*) nextResultSet
{
// if there are currently no results available, wait until
// 1) more results are available or
// 2) we know there are no more results available
NSInteger resultCount;
@synchronized(results){
resultCount = [results count];
}
while (!resultCount && !finishedCreatingResults) {
usleep(1000);
@synchronized(results){
resultCount = [results count];
}
}

// if there are no more results available, just return nil

if (!resultCount) return nil;

NSLog(@"Delivering a result.");

// return the next result and remove it from the stack
MCPStreamingResult *nextResult;
@synchronized(results){
nextResult = [[results objectAtIndex:0] retain];
[results removeObjectAtIndex:0];
}
return [nextResult autorelease];
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ typedef struct SP_MYSQL_ROWS {

@interface MCPStreamingResult : MCPResult
{
MCPConnection *parentConnection;
id parentConnection;

MYSQL_FIELD *fieldDefinitions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ @implementation MCPStreamingResult : MCPResult
* 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
- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(id)theConnection
{
return [self initWithMySQLPtr:mySQLPtr encoding:theEncoding timeZone:theTimeZone connection:theConnection withFullStreaming:NO];
}
Expand All @@ -67,7 +67,7 @@ - (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding
* Master initialisation method, allowing selection of either full streaming or safe streaming
* (see "important note" above)
*/
- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection withFullStreaming:(BOOL)useFullStreaming
- (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(id)theConnection withFullStreaming:(BOOL)useFullStreaming
{
if ((self = [super init])) {
mEncoding = theEncoding;
Expand Down Expand Up @@ -419,6 +419,8 @@ @implementation MCPStreamingResult (PrivateAPI)
- (void)_downloadAllData
{
NSAutoreleasePool *downloadPool = [[NSAutoreleasePool alloc] init];
[[NSThread currentThread] setName:@"MCPStreaming Result Data Download Thread"];

MYSQL_ROW theRow;
unsigned long *fieldLengths;
NSInteger i, dataCopiedLength, rowDataLength;
Expand Down Expand Up @@ -493,6 +495,8 @@ - (void)_downloadAllData
- (void) _freeAllDataWhenDone
{
NSAutoreleasePool *dataFreeingPool = [[NSAutoreleasePool alloc] init];

[[NSThread currentThread] setName:@"MCPStreaming Result Data Free Thread"];

while (!dataDownloaded || freedRowCount != downloadedRowCount) {

Expand Down
Binary file modified multiple-results/Resources/English.lproj/DBView.strings
Binary file not shown.
Binary file modified multiple-results/Resources/English.lproj/Localizable.strings
Binary file not shown.
Loading

0 comments on commit 7f1b482

Please sign in to comment.