diff options
author | rowanbeentje <rowan@beent.je> | 2012-03-14 01:16:18 +0000 |
---|---|---|
committer | rowanbeentje <rowan@beent.je> | 2012-03-14 01:16:18 +0000 |
commit | 79eff5bf42154da8d7730e0e0159160f68ec4e16 (patch) | |
tree | 38db570f7c36fbe2995774fefa627f1b467a9371 /Frameworks/SPMySQLFramework/Source | |
parent | d5e20720cf7f991a691d9a03e7f895211b7c98ad (diff) | |
download | sequelpro-79eff5bf42154da8d7730e0e0159160f68ec4e16.tar.gz sequelpro-79eff5bf42154da8d7730e0e0159160f68ec4e16.tar.bz2 sequelpro-79eff5bf42154da8d7730e0e0159160f68ec4e16.zip |
Final feature work on the SPMySQL branch before merging:
- Add a ping keepalive managing object to prevent retain cycles from the NSTimer
- Add -[SPMySQLConnection copy] support
- Refactor Hans-Jörg Bibiko's database structure retrieval, moving it out of the MySQL framework and building it around a copy of the connection. This reduces the amount of connections-over-time used by Sequel Pro to two constant connections (addressing Issue #1097) and improves robustness.
- Use the database structure retrieval connection for faster query cancellation without an extra connection required, if possible
Diffstat (limited to 'Frameworks/SPMySQLFramework/Source')
10 files changed, 295 insertions, 37 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h new file mode 100644 index 00000000..83d055d5 --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h @@ -0,0 +1,36 @@ +// +// $Id$ +// +// Copying.h +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 8, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at <http://code.google.com/p/sequel-pro/> + + +@interface SPMySQLConnection (Copying) <NSCopying> + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m new file mode 100644 index 00000000..c2df2a4b --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m @@ -0,0 +1,71 @@ +// +// $Id$ +// +// Copying.m +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 8, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "Copying.h" + +@implementation SPMySQLConnection (Copying) + +/** + * Provide a copy of the SPMySQLConnection instance. + * The copy should inherit the full setup, but will not inherit + * the connection state - it will not be connected, and any connection + * details such as the selected database/encoding will not be inherited. + * Note that any proxy will not be referenced in the new connection, and + * should also be set if desired. + */ +- (id)copyWithZone:(NSZone *)zone +{ + SPMySQLConnection *copy = [[[self class] allocWithZone:zone] init]; + + // Synthesized details + [copy setDelegate:delegate]; + [copy setHost:host]; + [copy setUsername:username]; + [copy setPassword:password]; + [copy setPort:port]; + [copy setUseSocket:useSocket]; + [copy setSocketPath:socketPath]; + [copy setUseSSL:useSSL]; + [copy setSslKeyFilePath:sslKeyFilePath]; + [copy setSslCertificatePath:sslCertificatePath]; + [copy setSslCACertificatePath:sslCACertificatePath]; + [copy setTimeout:timeout]; + [copy setUseKeepAlive:useKeepAlive]; + [copy setRetryQueriesOnConnectionFailure:retryQueriesOnConnectionFailure]; + [copy setDelegateQueryLogging:delegateQueryLogging]; + + // Active connection state details, like selected database and encoding, are *not* copied. + + return copy; +} + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h index 3788c653..e84c4ca6 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h @@ -40,11 +40,8 @@ typedef struct { @interface SPMySQLConnection (Ping_and_KeepAlive) -// Setup functions -- (void)_initKeepAlivePingTimer; - // Keepalive ping initialisation -- (void)_keepAlive:(NSTimer *)theTimer; +- (void)_keepAlive; - (void)_threadedKeepAlive; // Master ping method diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m index 9e25edcb..3ce0c0cd 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m @@ -38,19 +38,6 @@ @implementation SPMySQLConnection (Ping_and_KeepAlive) #pragma mark - -#pragma mark Setup functions - -/** - * Set up the keepalive timer; this should be called on the main - * thread, to ensure the timer isn't descheduled when child threads - * terminate. - */ -- (void)_initKeepAlivePingTimer -{ - keepAliveTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(_keepAlive:) userInfo:nil repeats:YES] retain]; -} - -#pragma mark - #pragma mark Keepalive ping initialisation /** @@ -58,7 +45,7 @@ * This method is called every ten seconds and spawns a thread which determines * whether or not it should perform a ping. */ -- (void)_keepAlive:(NSTimer *)theTimer +- (void)_keepAlive { // Do nothing if not connected or if keepalive is disabled diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h index 9ab8bc6a..0f086a89 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h @@ -61,7 +61,6 @@ // Query cancellation - (void)cancelCurrentQuery; -- (BOOL)lastQueryWasCancelled; - (BOOL)lastQueryWasCancelledUsingReconnect; @end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m index cb5ce70d..9b54029c 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m @@ -282,7 +282,7 @@ } else { // Prevent retries if the query was cancelled or not a connection error - if (lastQueryWasCancelled && ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) { + if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) { break; } } @@ -549,14 +549,6 @@ } /** - * Returns whether the last query was cancelled using cancelCurrentQuery. - */ -- (BOOL)lastQueryWasCancelled -{ - return lastQueryWasCancelled; -} - -/** * If the last query was cancelled, returns whether that query cancellation * required the connection to be reset or whether the query was successfully * cancelled leaving the connection intact. diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h index 5d1a5d11..8f3b7f9f 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h @@ -30,6 +30,8 @@ // // More info at <http://code.google.com/p/sequel-pro/> +@class SPMySQLKeepAliveTimer; + @interface SPMySQLConnection : NSObject { // Delegate @@ -80,7 +82,7 @@ // Timeout and keep-alive NSUInteger timeout; BOOL useKeepAlive; - NSTimer *keepAliveTimer; + SPMySQLKeepAliveTimer *keepAliveTimer; CGFloat keepAliveInterval; uint64_t lastKeepAliveTime; NSUInteger keepAlivePingFailures; @@ -153,6 +155,8 @@ @property (readwrite, assign) BOOL delegateQueryLogging; +@property (readwrite, assign) BOOL lastQueryWasCancelled; + #pragma mark - #pragma mark Connection and disconnection diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m index 4968266d..4f1e8a74 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m @@ -31,6 +31,7 @@ // More info at <http://code.google.com/p/sequel-pro/> #import "SPMySQL Private APIs.h" +#import "SPMySQLKeepAliveTimer.h" #include <mach/mach_time.h> #include <pthread.h> #include <SystemConfiguration/SCNetworkReachability.h> @@ -71,6 +72,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS @synthesize mysqlConnectionThreadId; @synthesize retryQueriesOnConnectionFailure; @synthesize delegateQueryLogging; +@synthesize lastQueryWasCancelled; #pragma mark - #pragma mark Initialisation and teardown @@ -161,11 +163,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS retryQueriesOnConnectionFailure = YES; // Start the ping keepalive timer - if ([NSThread isMainThread]) { - [self _initKeepAlivePingTimer]; - } else { - [self performSelectorOnMainThread:@selector(_initKeepAlivePingTimer) withObject:nil waitUntilDone:YES]; - } + keepAliveTimer = [[SPMySQLKeepAliveTimer alloc] initWithInterval:10 target:self selector:@selector(_keepAlive)]; } return self; @@ -181,6 +179,10 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS // Unset the delegate [self setDelegate:nil]; + // Clear the keepalive timer + [keepAliveTimer invalidate]; + [keepAliveTimer release]; + // Disconnect if appropriate (which should also disconnect any proxy) [self disconnect]; @@ -196,14 +198,12 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS } [connectionLock release], connectionLock = nil; - [encoding dealloc]; + [encoding release]; if (previousEncoding) [previousEncoding release], previousEncoding = nil; if (database) [database release], database = nil; if (serverVersionString) [serverVersionString release], serverVersionString = nil; if (queryErrorMessage) [queryErrorMessage release], queryErrorMessage = nil; - [keepAliveTimer invalidate]; - [keepAliveTimer release]; [delegateDecisionLock release]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h new file mode 100644 index 00000000..ebe46bed --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h @@ -0,0 +1,45 @@ +// +// $Id$ +// +// SPMySQLKeepAliveTimer.h +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 5, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at <http://code.google.com/p/sequel-pro/> + + +@interface SPMySQLKeepAliveTimer : NSObject { + id timerTarget; + SEL timerSelector; + NSTimeInterval timerRepeatInterval; + + NSTimer *wrappedTimer; +} + +- (id)initWithInterval:(NSTimeInterval)anInterval target:(id)aTarget selector:(SEL)aSelector; +- (void)invalidate; + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m new file mode 100644 index 00000000..f9164aff --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m @@ -0,0 +1,127 @@ +// +// $Id$ +// +// SPMySQLKeepAliveTimer.m +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 5, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at <http://code.google.com/p/sequel-pro/> + + +#import "SPMySQLKeepAliveTimer.h" +#import "SPMySQL Private APIs.h" + +@interface SPMySQLKeepAliveTimer (Private_API) + +- (void)_initKeepAliveTimer; +- (void)_forwardPing; + +@end + +#pragma mark - + +@implementation SPMySQLKeepAliveTimer + +/** + * Prevent SPMySQLKeepAliveTimer from being init'd normally. + */ +- (id)init +{ + [NSException raise:NSInternalInconsistencyException format:@"SPMySQLKeepAliveTimers should not be init'd directly; use initWithInterval:target:selector: instead."]; + return nil; +} + +/** + * Initialise the SPMySQLKeepAliveTimer. This also sets up the contained timer, + * which has to be wrapped in this class to prevent retain cycles preventing the + * parent connection from being released. + * + * After initialisation, the delegate should be set to ensure that the timer events + * are received. + */ +- (id)initWithInterval:(NSTimeInterval)anInterval target:(id)aTarget selector:(SEL)aSelector +{ + if ((self = [super init])) { + wrappedTimer = nil; + + // Keep a weak reference to the target + timerTarget = aTarget; + timerSelector = aSelector; + timerRepeatInterval = anInterval; + + // Ensure the timer is set up on the main thread + if ([NSThread isMainThread]) { + [self _initKeepAliveTimer]; + } else { + [self performSelectorOnMainThread:@selector(_initKeepAliveTimer) withObject:nil waitUntilDone:YES]; + } + } + + return self; +} + +/** + * Invalidate the wrapped timer, which also releases the reference to the timer + * target (this object), breaking retain loops. + */ +- (void)invalidate +{ + if ([NSThread isMainThread]) { + [wrappedTimer invalidate]; + } else { + [wrappedTimer performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES]; + } +} + +- (void)dealloc +{ + [wrappedTimer dealloc]; + [super dealloc]; +} + +@end + +@implementation SPMySQLKeepAliveTimer (Private_API) + +/** + * Set up the timer to tickle the target. This must be set up on the main thread + * to ensure the timer events keep firing. + */ +- (void)_initKeepAliveTimer +{ + wrappedTimer = [[NSTimer scheduledTimerWithTimeInterval:timerRepeatInterval target:self selector:@selector(_forwardPing) userInfo:nil repeats:YES] retain]; +} + +/** + * Forward the NSTimer-fired ping to the target object. Performing this forwarding + * breaks the retain cycle. + */ +- (void)_forwardPing +{ + [timerTarget performSelector:timerSelector]; +} + +@end |