aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2012-03-14 01:16:18 +0000
committerrowanbeentje <rowan@beent.je>2012-03-14 01:16:18 +0000
commit79eff5bf42154da8d7730e0e0159160f68ec4e16 (patch)
tree38db570f7c36fbe2995774fefa627f1b467a9371
parentd5e20720cf7f991a691d9a03e7f895211b7c98ad (diff)
downloadsequelpro-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
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj16
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h36
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m71
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h5
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m15
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h1
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m10
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m16
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h45
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m127
-rw-r--r--Source/SPDatabaseData.m1
-rw-r--r--Source/SPDatabaseDocument.h4
-rw-r--r--Source/SPDatabaseDocument.m21
-rw-r--r--Source/SPDatabaseStructure.h57
-rw-r--r--Source/SPDatabaseStructure.m647
-rw-r--r--Source/SPNarrowDownCompletion.h5
-rw-r--r--Source/SPNarrowDownCompletion.m15
-rw-r--r--Source/SPNavigatorController.m7
-rw-r--r--Source/SPTablesList.m4
-rw-r--r--Source/SPTextView.m11
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj14
22 files changed, 1075 insertions, 59 deletions
diff --git a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
index 933a68b7..2f8d300c 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
+++ b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj
@@ -28,6 +28,10 @@
584294FB14CB8002000F8438 /* Encoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 584294F914CB8002000F8438 /* Encoding.m */; };
584294FE14CB8002000F8438 /* Server Info.h in Headers */ = {isa = PBXBuildFile; fileRef = 584294FC14CB8002000F8438 /* Server Info.h */; settings = {ATTRIBUTES = (Public, ); }; };
584294FF14CB8002000F8438 /* Server Info.m in Sources */ = {isa = PBXBuildFile; fileRef = 584294FD14CB8002000F8438 /* Server Info.m */; };
+ 584D812E15057ECD00F24774 /* SPMySQLKeepAliveTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */; };
+ 584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */; };
+ 584D82551509775000F24774 /* Copying.h in Headers */ = {isa = PBXBuildFile; fileRef = 584D82531509775000F24774 /* Copying.h */; };
+ 584D82561509775000F24774 /* Copying.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D82541509775000F24774 /* Copying.m */; };
586A99FB14F02E21007F82BF /* SPMySQLStreamingResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 586A99F914F02E21007F82BF /* SPMySQLStreamingResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
586A99FC14F02E21007F82BF /* SPMySQLStreamingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 586A99FA14F02E21007F82BF /* SPMySQLStreamingResult.m */; };
586AA16714F30C5F007F82BF /* Convenience Methods.h in Headers */ = {isa = PBXBuildFile; fileRef = 586AA16514F30C5F007F82BF /* Convenience Methods.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -92,6 +96,10 @@
584294F914CB8002000F8438 /* Encoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Encoding.m; path = "Source/SPMySQLConnection Categories/Encoding.m"; sourceTree = "<group>"; };
584294FC14CB8002000F8438 /* Server Info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Server Info.h"; path = "Source/SPMySQLConnection Categories/Server Info.h"; sourceTree = "<group>"; };
584294FD14CB8002000F8438 /* Server Info.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "Server Info.m"; path = "Source/SPMySQLConnection Categories/Server Info.m"; sourceTree = "<group>"; };
+ 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLKeepAliveTimer.h; path = Source/SPMySQLKeepAliveTimer.h; sourceTree = "<group>"; };
+ 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLKeepAliveTimer.m; path = Source/SPMySQLKeepAliveTimer.m; sourceTree = "<group>"; };
+ 584D82531509775000F24774 /* Copying.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Copying.h; path = "Source/SPMySQLConnection Categories/Copying.h"; sourceTree = "<group>"; };
+ 584D82541509775000F24774 /* Copying.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Copying.m; path = "Source/SPMySQLConnection Categories/Copying.m"; sourceTree = "<group>"; };
586A99F914F02E21007F82BF /* SPMySQLStreamingResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLStreamingResult.h; path = Source/SPMySQLStreamingResult.h; sourceTree = "<group>"; };
586A99FA14F02E21007F82BF /* SPMySQLStreamingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLStreamingResult.m; path = Source/SPMySQLStreamingResult.m; sourceTree = "<group>"; };
586AA0E714F1CEC8007F82BF /* Readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.txt; sourceTree = "<group>"; };
@@ -202,6 +210,8 @@
58C7C1E314DB6E4C00436315 /* SPMySQLFastStreamingResult.m */,
58C7C1E114DB6E3000436315 /* Result Categories */,
580A331B14D75CCF000D6933 /* Result types */,
+ 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */,
+ 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */,
);
name = Classes;
sourceTree = "<group>";
@@ -288,6 +298,8 @@
584294EB14CB8002000F8438 /* Connection Categories */ = {
isa = PBXGroup;
children = (
+ 584D82531509775000F24774 /* Copying.h */,
+ 584D82541509775000F24774 /* Copying.m */,
58C00AB314E4892E00AC489A /* Delegate & Proxy.h */,
58C00AB414E4892E00AC489A /* Delegate & Proxy.m */,
58C00BCF14E7459600AC489A /* Databases & Tables.h */,
@@ -387,6 +399,8 @@
586A99FB14F02E21007F82BF /* SPMySQLStreamingResult.h in Headers */,
586AA16714F30C5F007F82BF /* Convenience Methods.h in Headers */,
586AA81414F6C648007F82BF /* SPMySQLArrayAdditions.h in Headers */,
+ 584D812E15057ECD00F24774 /* SPMySQLKeepAliveTimer.h in Headers */,
+ 584D82551509775000F24774 /* Copying.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -470,6 +484,8 @@
58C00BD214E7459600AC489A /* Databases & Tables.m in Sources */,
586A99FC14F02E21007F82BF /* SPMySQLStreamingResult.m in Sources */,
586AA16814F30C5F007F82BF /* Convenience Methods.m in Sources */,
+ 584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */,
+ 584D82561509775000F24774 /* Copying.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
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
diff --git a/Source/SPDatabaseData.m b/Source/SPDatabaseData.m
index 2e827fa9..2317902e 100644
--- a/Source/SPDatabaseData.m
+++ b/Source/SPDatabaseData.m
@@ -315,6 +315,7 @@ NSInteger _sortMySQL4CharsetEntry(NSDictionary *itemOne, NSDictionary *itemTwo,
if ([connection queryErrored]) return [NSArray array];
+ [result setReturnDataAsStrings:YES];
return [result getAllRows];
}
diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h
index 2a03e589..1c6fe296 100644
--- a/Source/SPDatabaseDocument.h
+++ b/Source/SPDatabaseDocument.h
@@ -33,7 +33,7 @@
#ifndef SP_REFACTOR /* class forward decls */
SPProcessListController, SPServerVariablesController, SPUserManager, SPWindowController,
#endif
-SPDatabaseData, SPTablesList, SPTableStructure, SPTableContent, SPTableData, SPServerSupport, SPCustomQuery, SPMySQLConnection;
+SPDatabaseData, SPTablesList, SPTableStructure, SPTableContent, SPTableData, SPServerSupport, SPCustomQuery, SPDatabaseStructure, SPMySQLConnection;
#import "SPConnectionControllerDelegateProtocol.h"
@@ -237,6 +237,7 @@ SPDatabaseData, SPTablesList, SPTableStructure, SPTableContent, SPTableData, SPS
NSString *processID;
BOOL windowTitleStatusViewIsVisible;
#endif
+ SPDatabaseStructure *databaseStructureRetrieval;
}
#ifdef SP_REFACTOR /* ivars */
@@ -268,6 +269,7 @@ SPDatabaseData, SPTablesList, SPTableStructure, SPTableContent, SPTableData, SPS
@property (readwrite, retain) NSString *processID;
#endif
@property (readonly) SPServerSupport *serverSupport;
+@property (readonly) SPDatabaseStructure *databaseStructureRetrieval;
#ifndef SP_REFACTOR /* method decls */
- (BOOL)isUntitled;
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index fef580ba..c8154c7f 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -60,6 +60,7 @@ enum {
#import "SPTableData.h"
#endif
#import "SPDatabaseData.h"
+#import "SPDatabaseStructure.h"
#ifndef SP_REFACTOR /* headers */
#import "SPAppController.h"
#import "SPExtendedTableInfo.h"
@@ -119,6 +120,7 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
#endif
@synthesize isProcessing;
@synthesize serverSupport;
+@synthesize databaseStructureRetrieval;
#ifndef SP_REFACTOR /* ivars */
@synthesize processID;
#endif
@@ -218,6 +220,8 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
[nibLoader release];
[nibObjectsToRelease addObjectsFromArray:dbViewTopLevelObjects];
#endif
+
+ databaseStructureRetrieval = [[SPDatabaseStructure alloc] initWithDelegate:self];
}
return self;
@@ -434,6 +438,9 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
[chooseDatabaseButton setEnabled:!_isWorkingLevel];
+ // Set the connection on the database structure builder
+ [databaseStructureRetrieval setConnectionToClone:mySQLConnection];
+
[databaseDataInstance setConnection:mySQLConnection];
// Pass the support class to the data instance
@@ -1357,7 +1364,15 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
if (!taskCanBeCancelled) return;
[taskCancelButton setEnabled:NO];
- [mySQLConnection cancelCurrentQuery];
+
+ // See whether there is an active database structure task and whether it can be used
+ // to cancel the query, for speed (no connection overhead!)
+ if (databaseStructureRetrieval && [databaseStructureRetrieval connection]) {
+ [mySQLConnection setLastQueryWasCancelled:YES];
+ [[databaseStructureRetrieval connection] killQueryOnThreadID:[mySQLConnection mysqlConnectionThreadId]];
+ } else {
+ [mySQLConnection cancelCurrentQuery];
+ }
if (taskCancellationCallbackObject && taskCancellationCallbackSelector) {
[taskCancellationCallbackObject performSelector:taskCancellationCallbackSelector];
@@ -5658,6 +5673,8 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
#endif
+ [databaseStructureRetrieval release];
+
[allDatabases release];
[allSystemDatabases release];
#ifndef SP_REFACTOR /* dealloc ivars */
@@ -5879,7 +5896,7 @@ static NSString *SPCreateSyntx = @"SPCreateSyntax";
// This only deletes the db and refreshes the navigator since nothing is changed
// that's why we can run this on main thread
- [mySQLConnection queryDbStructureWithUserInfo:nil];
+ [databaseStructureRetrieval queryDbStructureWithUserInfo:nil];
// Delete was successful
if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil;
diff --git a/Source/SPDatabaseStructure.h b/Source/SPDatabaseStructure.h
new file mode 100644
index 00000000..2b2d6977
--- /dev/null
+++ b/Source/SPDatabaseStructure.h
@@ -0,0 +1,57 @@
+//
+// $Id$
+//
+// SPDatabaseStructure.h
+// sequel-pro
+//
+// Created by Hans-Jörg Bibiko on March 25, 2010
+// Copyright (c) 2010 Hans-Jörg Bibiko. 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 <http://code.google.com/p/sequel-pro/>
+
+@class SPMySQLConnection, SPDatabaseDocument;
+
+@interface SPDatabaseStructure : NSObject {
+ SPDatabaseDocument *delegate;
+ SPMySQLConnection *mySQLConnection;
+
+ NSMutableDictionary *structure;
+ NSMutableArray *allKeysofDbStructure;
+
+ NSMutableArray *structureRetrievalThreads;
+
+ pthread_mutex_t threadManagementLock;
+ pthread_mutex_t dataLock;
+ pthread_mutex_t connectionCheckLock;
+}
+
+// Setup
+- (id)initWithDelegate:(SPDatabaseDocument *)theDelegate;
+- (void)setConnectionToClone:(SPMySQLConnection *)aConnection;
+
+// Information
+- (SPMySQLConnection *)connection;
+
+// Structure retrieval from the server
+- (void)queryDbStructureWithUserInfo:(NSDictionary*)userInfo;
+- (BOOL)isQueryingDatabaseStructure;
+
+// Structure information
+- (NSDictionary *)structure;
+- (NSArray *)allStructureKeys;
+
+@end
diff --git a/Source/SPDatabaseStructure.m b/Source/SPDatabaseStructure.m
new file mode 100644
index 00000000..35637093
--- /dev/null
+++ b/Source/SPDatabaseStructure.m
@@ -0,0 +1,647 @@
+//
+// $Id$
+//
+// SPDatabaseStructure.m
+// sequel-pro
+//
+// Created by Hans-Jörg Bibiko on March 25, 2010
+// Copyright (c) 2010 Hans-Jörg Bibiko. 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 <http://code.google.com/p/sequel-pro/>
+
+#import "SPDatabaseStructure.h"
+#import "SPDatabaseDocument.h"
+#import "SPConnectionDelegate.h"
+#import "SPTablesList.h"
+#import "RegexKitLite.h"
+#import "SPMySQL.h"
+#import <pthread.h>
+
+@interface SPDatabaseStructure (Private_API)
+
+- (void)_updateGlobalVariablesWithStructure:(NSDictionary *)aStructure keys:(NSArray *)theKeys;
+- (void)_cloneConnectionFromConnection:(SPMySQLConnection *)aConnection;
+- (BOOL)_ensureConnection;
+
+@end
+
+#pragma mark -
+
+@implementation SPDatabaseStructure
+
+#pragma mark -
+#pragma mark Setup and teardown
+
+/**
+ * Prevent SPDatabaseStructure from being init'd normally.
+ */
+- (id)init
+{
+ [NSException raise:NSInternalInconsistencyException format:@"SPDatabaseStructures should not be init'd directly; use initWithDelegate: instead."];
+ return nil;
+}
+
+/**
+ * Standard init method, constructing the SPDatabaseStructure around a SPMySQL
+ * connection pointer and a delegate.
+ */
+- (id)initWithDelegate:(SPDatabaseDocument *)theDelegate
+{
+ if ((self = [super init])) {
+
+ // Keep a weak reference to the delegate
+ delegate = theDelegate;
+
+ // Start with no root connection
+ mySQLConnection = nil;
+
+ // Set up empty structure and keys storage
+ structureRetrievalThreads = [[NSMutableArray alloc] init];
+ structure = [[NSMutableDictionary alloc] initWithCapacity:1];
+ allKeysofDbStructure = [[NSMutableArray alloc] initWithCapacity:20];
+
+ // Set up the connection, thread management and data locks
+ pthread_mutex_init(&threadManagementLock, NULL);
+ pthread_mutex_init(&dataLock, NULL);
+ pthread_mutex_init(&connectionCheckLock, NULL);
+ }
+
+ return self;
+}
+
+/**
+ * Rather than supplying a connection to SPDatabaseStructure, the class instead
+ * will set up its own connection to allow background querying. The supplied
+ * connection will be used to look up details for the clone process.
+ */
+- (void)setConnectionToClone:(SPMySQLConnection *)aConnection
+{
+
+ // Perform the task in a background thread to avoid blocking the UI
+ [NSThread detachNewThreadSelector:@selector(_cloneConnectionFromConnection:) toTarget:self withObject:aConnection];
+}
+
+- (void)dealloc
+{
+
+ // Ensure all the retrieval threads have ended
+ pthread_mutex_lock(&threadManagementLock);
+ if ([structureRetrievalThreads count]) {
+ for (NSThread *eachThread in structureRetrievalThreads) {
+ [eachThread cancel];
+ }
+ while ([structureRetrievalThreads count]) {
+ pthread_mutex_unlock(&threadManagementLock);
+ usleep(100000);
+ pthread_mutex_lock(&threadManagementLock);
+ }
+ }
+ pthread_mutex_unlock(&threadManagementLock);
+ [structureRetrievalThreads release];
+
+ pthread_mutex_destroy(&threadManagementLock);
+ pthread_mutex_destroy(&dataLock);
+ pthread_mutex_destroy(&connectionCheckLock);
+ delegate = nil;
+
+ if (mySQLConnection) [mySQLConnection release], mySQLConnection = nil;
+ if (structure) [structure release], structure = nil;
+ if (allKeysofDbStructure) [allKeysofDbStructure release], allKeysofDbStructure = nil;
+
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Information
+
+- (SPMySQLConnection *)connection
+{
+ return mySQLConnection;
+}
+
+#pragma mark -
+#pragma mark Structure retrieval from the server
+
+/**
+ * Updates the dict containing the structure of all available databases (mainly for completion/navigator)
+ * executed on the helper connection.
+ * Should always be executed on a background thread.
+ */
+- (void)queryDbStructureWithUserInfo:(NSDictionary*)userInfo
+{
+ NSAutoreleasePool *queryPool = [[NSAutoreleasePool alloc] init];
+ BOOL structureWasUpdated = NO;
+
+ // Lock the management lock
+ pthread_mutex_lock(&threadManagementLock);
+
+ // If 'cancelQuerying' is set try to interrupt any current querying
+ if (userInfo && [userInfo objectForKey:@"cancelQuerying"]) {
+ for (NSThread *eachThread in structureRetrievalThreads) {
+ [eachThread cancel];
+ }
+ }
+
+ // Add this thread to the group
+ [structureRetrievalThreads addObject:[NSThread currentThread]];
+
+ // Only allow one request to be running against the server at any one time, to prevent
+ // escessive server i/o or slowdown. Loop until this is the first thread in the array
+ while ([structureRetrievalThreads objectAtIndex:0] != [NSThread currentThread]) {
+ if ([[NSThread currentThread] isCancelled]) {
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+ [queryPool release];
+ return;
+ }
+
+ pthread_mutex_unlock(&threadManagementLock);
+ usleep(1000000);
+ pthread_mutex_lock(&threadManagementLock);
+ }
+ pthread_mutex_unlock(&threadManagementLock);
+
+ // This thread is now first on the stack, and about to process the structure.
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureIsUpdating" object:delegate];
+
+ NSString *connectionID;
+ if([delegate respondsToSelector:@selector(connectionID)])
+ connectionID = [NSString stringWithString:[delegate connectionID]];
+ else
+ connectionID = @"_";
+
+ // Re-init with already cached data from navigator controller
+ NSMutableDictionary *queriedStructure = [NSMutableDictionary dictionary];
+ NSDictionary *dbstructure = [delegate getDbStructure];
+ if (dbstructure) [queriedStructure setDictionary:[NSMutableDictionary dictionaryWithDictionary:dbstructure]];
+
+ NSMutableArray *queriedStructureKeys = [NSMutableArray array];
+ NSArray *dbStructureKeys = [delegate allSchemaKeys];
+ if (dbStructureKeys) [queriedStructureKeys setArray:dbStructureKeys];
+
+ // Retrieve all the databases known of by the delegate
+ NSMutableArray *connectionDatabases = [NSMutableArray array];
+ [connectionDatabases addObjectsFromArray:[delegate allSystemDatabaseNames]];
+ [connectionDatabases addObjectsFromArray:[delegate allDatabaseNames]];
+
+ // Add all known databases coming from connection if they aren't parsed yet
+ for (id db in connectionDatabases) {
+ NSString *dbid = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db];
+ if(![queriedStructure objectForKey:dbid]) {
+ structureWasUpdated = YES;
+ [queriedStructure setObject:db forKey:dbid];
+ [queriedStructureKeys addObject:dbid];
+ }
+ }
+
+ // Check the existing databases in the 'structure' and 'allKeysOfDbStructure' stores,
+ // and remove any that are no longer found in the connectionDatabases list (indicating deletion).
+ // Iterate through extracted keys to avoid <NSCFDictionary> mutation while being enumerated.
+ NSArray *keys = [queriedStructure allKeys];
+ for(id key in keys) {
+ NSString *db = [[key componentsSeparatedByString:SPUniqueSchemaDelimiter] objectAtIndex:1];
+ if(![connectionDatabases containsObject:db]) {
+ structureWasUpdated = YES;
+ [queriedStructure removeObjectForKey:key];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", key, SPUniqueSchemaDelimiter]];
+ [queriedStructureKeys filterUsingPredicate:predicate];
+ [queriedStructureKeys removeObject:key];
+ }
+ }
+
+ NSString *currentDatabase = nil;
+ if ([delegate respondsToSelector:@selector(database)])
+ currentDatabase = [delegate database];
+
+ // Determine whether the database details need to be queried.
+ BOOL shouldQueryStructure = YES;
+ NSString *db_id = nil;
+
+ // If no database is selected, no need to check further
+ if(!currentDatabase || (currentDatabase && ![currentDatabase length])) {
+ shouldQueryStructure = NO;
+
+ // Otherwise, build up the schema key for the database to be retrieved.
+ } else {
+ db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, currentDatabase];
+
+ // Check to see if a cache already exists for the database.
+ if ([queriedStructure objectForKey:db_id] && [[queriedStructure objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
+
+ // The cache is available. If the `mysql` or `information_schema` databases are being queried,
+ // never requery as their structure will never change.
+ // 5.5.3+ also has performance_schema meta database
+ if ([currentDatabase isEqualToString:@"mysql"] || [currentDatabase isEqualToString:@"information_schema"] || [currentDatabase isEqualToString:@"performance_schema"]) {
+ shouldQueryStructure = NO;
+
+ // Otherwise, if the forceUpdate flag wasn't supplied or evaluates to false, also don't update.
+ } else if (userInfo == nil || ![userInfo objectForKey:@"forceUpdate"] || ![[userInfo objectForKey:@"forceUpdate"] boolValue]) {
+ shouldQueryStructure = NO;
+ }
+ }
+ }
+
+ // If it has been determined that no new structure needs to be retrieved, clean up and return.
+ if (!shouldQueryStructure) {
+
+ // Update the global variables
+ [self _updateGlobalVariablesWithStructure:queriedStructure keys:queriedStructureKeys];
+
+ if (structureWasUpdated) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+ }
+
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+ return;
+ }
+
+ // Retrieve the tables and views for this database from SPTablesList
+ NSMutableArray *tablesAndViews = [NSMutableArray array];
+ for (id aTable in [[delegate valueForKeyPath:@"tablesListInstance"] allTableNames]) {
+ NSDictionary *aTableDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ aTable, @"name",
+ @"0", @"type",
+ nil];
+ [tablesAndViews addObject:aTableDict];
+ }
+ for (id aView in [[delegate valueForKeyPath:@"tablesListInstance"] allViewNames]) {
+ NSDictionary *aViewDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ aView, @"name",
+ @"1", @"type",
+ nil];
+ [tablesAndViews addObject:aViewDict];
+ }
+
+ // Do not parse more than 2000 tables/views per db
+ if ([tablesAndViews count] > 2000) {
+ NSLog(@"%lu items in database %@. Only 2000 items can be parsed. Stopped parsing.", (unsigned long)[tablesAndViews count], currentDatabase);
+
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+ return;
+ }
+
+ // For future usage - currently unused
+ // If the affected item name and type - for example, table type and table name - were supplied, extract it.
+ NSString *affectedItem = nil;
+ NSInteger affectedItemType = -1;
+ if(userInfo && [userInfo objectForKey:@"affectedItem"]) {
+ affectedItem = [userInfo objectForKey:@"affectedItem"];
+ if([userInfo objectForKey:@"affectedItemType"])
+ affectedItemType = [[userInfo objectForKey:@"affectedItemType"] intValue];
+ else
+ affectedItem = nil;
+ }
+
+ // Delete all stored data for the database to be updated, leaving the structure key
+ [queriedStructure removeObjectForKey:db_id];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", db_id, SPUniqueSchemaDelimiter]];
+ [queriedStructureKeys filterUsingPredicate:predicate];
+
+ // Set up the database as an empty mutable dictionary ready for tables, and store a reference
+ [queriedStructure setObject:[NSMutableDictionary dictionary] forKey:db_id];
+ NSMutableDictionary *databaseStructure = [queriedStructure objectForKey:db_id];
+
+ NSString *currentDatabaseEscaped = [currentDatabase stringByReplacingOccurrencesOfString:@"`" withString:@"``"];
+
+ NSUInteger uniqueCounter = 0; // used to make field data unique
+ SPMySQLResult *theResult;
+
+ // Loop through the known tables and views, retrieving details for each
+ for (NSDictionary *aTableDict in tablesAndViews) {
+
+ // Extract the name
+ NSString *aTableName = [aTableDict objectForKey:@"name"];
+
+ if(!aTableName) continue;
+ if(![aTableName isKindOfClass:[NSString class]]) continue;
+ if(![aTableName length]) continue;
+
+ BOOL cancelThread = NO;
+
+ // If the thread has been cancelled, abort without saving
+ if ([[NSThread currentThread] isCancelled]) cancelThread = YES;
+
+ // Check connection state before use
+ while (!cancelThread && pthread_mutex_trylock(&connectionCheckLock)) {
+ usleep(100000);
+ if ([[NSThread currentThread] isCancelled]) {
+ cancelThread = YES;
+ break;
+ }
+ }
+
+ if (cancelThread) {
+ pthread_mutex_trylock(&connectionCheckLock);
+ pthread_mutex_unlock(&connectionCheckLock);
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+ return;
+ }
+
+ if (![self _ensureConnection]) {
+ pthread_mutex_unlock(&connectionCheckLock);
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+ return;
+ }
+ pthread_mutex_unlock(&connectionCheckLock);
+
+ // Retrieve the column details
+ theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`", [aTableName stringByReplacingOccurrencesOfString:@"`" withString:@"``"], currentDatabaseEscaped]];
+ if (!theResult) {
+ continue;
+ }
+ [theResult setDefaultRowReturnType:SPMySQLResultRowAsArray];
+ [theResult setReturnDataAsStrings:YES];
+
+ // Add a structure key for this table
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, aTableName];
+ [queriedStructureKeys addObject:table_id];
+
+ // Add a mutable dictionary to the structure and store a reference
+ [databaseStructure setObject:[NSMutableDictionary dictionary] forKey:table_id];
+ NSMutableDictionary *tableStructure = [databaseStructure objectForKey:table_id];
+
+ // Loop through the fields, extracting details for each
+ for (NSArray *row in theResult) {
+ NSString *field = [row objectAtIndex:0];
+ NSString *type = [row objectAtIndex:1];
+ NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
+ NSString *collation = [row objectAtIndex:2];
+ NSString *isnull = [row objectAtIndex:3];
+ NSString *key = [row objectAtIndex:4];
+ NSString *def = [row objectAtIndex:5];
+ NSString *extra = [row objectAtIndex:6];
+ NSString *priv = [row objectAtIndex:7];
+ NSString *comment;
+ if ([row count] > 8) {
+ comment = [row objectAtIndex:8];
+ } else {
+ comment = @"";
+ }
+
+ NSString *charset = @"";
+ if (![collation isNSNull]) {
+ NSArray *a = [collation componentsSeparatedByString:@"_"];
+ charset = [a objectAtIndex:0];
+ }
+
+ // Add a structure key for this field
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
+ [queriedStructureKeys addObject:field_id];
+
+ [tableStructure setObject:[NSArray arrayWithObjects:type, def, isnull, charset, collation, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [tableStructure setObject:[aTableDict objectForKey:@"type"] forKey:@" struct_type "];
+ uniqueCounter++;
+ }
+
+ // Allow a tiny pause between iterations
+ usleep(10);
+ }
+
+ // If the MySQL version is higher than 5, also retrieve function/procedure details via the information_schema table
+ if ([mySQLConnection serverMajorVersion] >= 5) {
+ BOOL cancelThread = NO;
+
+ if ([[NSThread currentThread] isCancelled]) cancelThread = YES;
+
+ // Check connection state before use
+ while (!cancelThread && pthread_mutex_trylock(&connectionCheckLock)) {
+ usleep(100000);
+ if ([[NSThread currentThread] isCancelled]) {
+ cancelThread = YES;
+ break;
+ }
+ }
+
+ if (!cancelThread) {
+ if (![self _ensureConnection]) cancelThread = YES;
+ pthread_mutex_unlock(&connectionCheckLock);
+ };
+
+ // Return if the thread is due to be cancelled
+ if (cancelThread) {
+ pthread_mutex_trylock(&connectionCheckLock);
+ pthread_mutex_unlock(&connectionCheckLock);
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+ return;
+ }
+
+ // Retrieve the column details
+ theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `information_schema`.`ROUTINES` WHERE `information_schema`.`ROUTINES`.`ROUTINE_SCHEMA` = '%@'", [currentDatabase stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]]];
+ [theResult setDefaultRowReturnType:SPMySQLResultRowAsArray];
+
+ // Loop through the rows and extract the function details
+ for (NSArray *row in theResult) {
+ NSString *fname = [row objectAtIndex:0];
+ NSString *type = ([[row objectAtIndex:4] isEqualToString:@"FUNCTION"]) ? @"3" : @"2";
+ NSString *dtd = [row objectAtIndex:5];
+ NSString *det = [row objectAtIndex:11];
+ NSString *dataaccess = [row objectAtIndex:12];
+ NSString *security_type = [row objectAtIndex:14];
+ NSString *definer = [row objectAtIndex:19];
+
+ // Generate "table" and "field" names and add to structure key store
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, fname];
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, fname];
+ [queriedStructureKeys addObject:table_id];
+ [queriedStructureKeys addObject:field_id];
+
+ // Ensure that a dictionary exists for this "table" name
+ if(![[queriedStructure valueForKey:db_id] valueForKey:table_id])
+ [[queriedStructure valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
+
+ // Add the "field" details
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:
+ [NSArray arrayWithObjects:dtd, dataaccess, det, security_type, definer, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:type forKey:@" struct_type "];
+ uniqueCounter++;
+ }
+ }
+
+ // Update the global variables
+ [self _updateGlobalVariablesWithStructure:queriedStructure keys:queriedStructureKeys];
+
+ // Notify that the structure querying has been performed
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+
+ // Remove this thread from the processing stack
+ pthread_mutex_lock(&threadManagementLock);
+ [structureRetrievalThreads removeObject:[NSThread currentThread]];
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [queryPool release];
+}
+
+- (BOOL)isQueryingDatabaseStructure
+{
+ pthread_mutex_lock(&threadManagementLock);
+ BOOL returnValue = ([structureRetrievalThreads count] > 0);
+ pthread_mutex_unlock(&threadManagementLock);
+
+ return returnValue;
+}
+
+#pragma mark -
+#pragma mark Structure information
+
+/**
+ * Returns a dict containing the structure of all available databases
+ */
+- (NSDictionary *)structure
+{
+ pthread_mutex_lock(&dataLock);
+ NSDictionary *d = [NSDictionary dictionaryWithDictionary:structure];
+ pthread_mutex_unlock(&dataLock);
+
+ return d;
+}
+
+/**
+ * Returns all keys of the db structure
+ */
+- (NSArray *)allStructureKeys
+{
+ pthread_mutex_lock(&dataLock);
+ NSArray *r = [NSArray arrayWithArray:allKeysofDbStructure];
+ pthread_mutex_unlock(&dataLock);
+
+ return r;
+}
+
+#pragma mark -
+#pragma mark SPMySQLConnection delegate methods
+
+/**
+ * Forward keychain password requests to the database object.
+ */
+- (NSString *)keychainPasswordForConnection:(id)connection
+{
+ return [delegate keychainPasswordForConnection:connection];
+}
+
+@end
+
+#pragma mark -
+#pragma mark Private API
+
+@implementation SPDatabaseStructure (Private_API)
+
+/**
+ * Update the global variables, using the data lock for multithreading safety.
+ */
+- (void)_updateGlobalVariablesWithStructure:(NSDictionary *)aStructure keys:(NSArray *)theKeys
+{
+
+ NSString *connectionID = [delegate connectionID];
+
+ // Return if the delegate indicates disconnection
+ if([connectionID length] < 2) return;
+
+ pthread_mutex_lock(&dataLock);
+
+ [structure setObject:aStructure forKey:connectionID];
+ [allKeysofDbStructure setArray:theKeys];
+
+ pthread_mutex_unlock(&dataLock);
+}
+
+/**
+ * Set up a new connection in a background thread
+ */
+- (void)_cloneConnectionFromConnection:(SPMySQLConnection *)aConnection
+{
+ NSAutoreleasePool *connectionPool = [[NSAutoreleasePool alloc] init];
+
+ pthread_mutex_lock(&connectionCheckLock);
+
+ // If a connection is already set, ensure it's idle before releasing it
+ if (mySQLConnection) {
+ pthread_mutex_lock(&threadManagementLock);
+ if ([structureRetrievalThreads count]) {
+ for (NSThread *eachThread in structureRetrievalThreads) {
+ [eachThread cancel];
+ }
+ while ([structureRetrievalThreads count]) {
+ pthread_mutex_unlock(&threadManagementLock);
+ usleep(100000);
+ pthread_mutex_lock(&threadManagementLock);
+ }
+ }
+ pthread_mutex_unlock(&threadManagementLock);
+
+ [mySQLConnection release];
+ mySQLConnection = nil;
+ }
+
+ // Create a copy of the supplied connection
+ mySQLConnection = [aConnection copy];
+
+ // Set the delegate to this instance
+ [mySQLConnection setDelegate:self];
+
+ // Trigger the connection
+ [self _ensureConnection];
+
+ pthread_mutex_unlock(&connectionCheckLock);
+
+ [connectionPool drain];
+}
+
+- (BOOL)_ensureConnection
+{
+ if (!mySQLConnection) return NO;
+
+ // Check the connection state
+ if ([mySQLConnection isConnected] && [mySQLConnection checkConnection]) return YES;
+
+ // The connection isn't connected. Check the parent connection state, and if that
+ // also isn't connected, return.
+ if (![[delegate getConnection] isConnected]) return NO;
+
+ // Copy the local port from the parent connection, in case a proxy has changed
+ [mySQLConnection setPort:[[delegate getConnection] port]];
+
+ // Attempt a connection
+ if (![mySQLConnection connect]) return NO;
+
+ // Ensure the encoding is set to UTF8
+ [mySQLConnection setEncoding:@"utf8"];
+
+ // Return success
+ return YES;
+}
+
+@end \ No newline at end of file
diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h
index 44118380..5c22df2d 100644
--- a/Source/SPNarrowDownCompletion.h
+++ b/Source/SPNarrowDownCompletion.h
@@ -26,6 +26,8 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
+@class SPDatabaseStructure;
+
#ifndef SP_REFACTOR
@interface SPNarrowDownCompletion : NSWindow
#else
@@ -74,6 +76,7 @@
NSMutableCharacterSet* textualInputCharacters;
+ SPDatabaseStructure *databaseStructureRetrieval;
#ifndef SP_REFACTOR
NSUserDefaults *prefs;
#endif
@@ -85,7 +88,7 @@
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
- alias:(NSString*)anAlias isQueryingDBStructure:(BOOL)isQueryingDBStructure;
+ alias:(NSString*)anAlias withDBStructureRetriever:(SPDatabaseStructure *)theDatabaseStructure;
- (void)setCaretPos:(NSPoint)aPos;
- (void)insert_text:(NSString* )aString;
- (void)insertAutocompletePlaceholder;
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index 5e7b5320..79deb013 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -35,6 +35,7 @@
#import "RegexKitLite.h"
#import "SPTextView.h"
#import "SPQueryDocumentsController.h"
+#import "SPDatabaseStructure.h"
#pragma mark -
#pragma mark attribute definition
@@ -165,6 +166,7 @@
if(suggestions) [suggestions release];
if (filtered) [filtered release];
+ if (databaseStructureRetrieval) [databaseStructureRetrieval release];
[super dealloc];
}
@@ -193,7 +195,7 @@
timeCounter++;
if(timeCounter > 20) {
timeCounter = 0;
- if(![[theView valueForKeyPath:@"mySQLConnection"] isQueryingDatabaseStructure]) {
+ if(![databaseStructureRetrieval isQueryingDatabaseStructure]) {
isQueryingDatabaseStructure = NO;
if(stateTimer) {
[stateTimer invalidate];
@@ -229,7 +231,7 @@
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
- alias:(NSString*)anAlias isQueryingDBStructure:(BOOL)isQueryingDBStructure
+ alias:(NSString*)anAlias withDBStructureRetriever:(SPDatabaseStructure *)theDatabaseStructure
{
if((self = [self init]))
{
@@ -245,10 +247,6 @@
theAliasName = anAlias;
oneColumnMode = oneColumn;
- isQueryingDatabaseStructure = isQueryingDBStructure;
-
- if(isQueryingDatabaseStructure)
- stateTimer = [[NSTimer scheduledTimerWithTimeInterval:0.07f target:self selector:@selector(updateSyncArrowStatus) userInfo:nil repeats:YES] retain];
fuzzyMode = fuzzySearch;
if(fuzzyMode)
@@ -306,6 +304,11 @@
if(someAdditionalWordCharacters)
[textualInputCharacters addCharactersInString:someAdditionalWordCharacters];
+ databaseStructureRetrieval = [theDatabaseStructure retain];
+ isQueryingDatabaseStructure = [databaseStructureRetrieval isQueryingDatabaseStructure];
+
+ if(isQueryingDatabaseStructure)
+ stateTimer = [[NSTimer scheduledTimerWithTimeInterval:0.07f target:self selector:@selector(updateSyncArrowStatus) userInfo:nil repeats:YES] retain];
}
return self;
}
diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m
index 63c3ed51..2b27a598 100644
--- a/Source/SPNavigatorController.m
+++ b/Source/SPNavigatorController.m
@@ -34,6 +34,7 @@
#import "SPAppController.h"
#import "SPDatabaseViewController.h"
#import "SPMySQL.h"
+#import "SPDatabaseStructure.h"
#import <objc/message.h>
#endif
@@ -447,12 +448,12 @@ static NSComparisonResult compareStrings(NSString *s1, NSString *s2, void* conte
[[schemaData objectForKey:connectionName] removeObjectForKey:db];
}
}
- id structureData = [theConnection getDbStructure];
+ id structureData = [[doc databaseStructureRetrieval] structure];
if(structureData && [structureData objectForKey:connectionName] && [[structureData objectForKey:connectionName] isKindOfClass:NSDictionaryClass]) {
for(id item in [[structureData objectForKey:connectionName] allKeys])
[[schemaData objectForKey:connectionName] setObject:[[structureData objectForKey:connectionName] objectForKey:item] forKey:item];
- NSArray *a = [theConnection getAllKeysOfDbStructure];
+ NSArray *a = [[doc databaseStructureRetrieval] allStructureKeys];
if(a)
[allSchemaKeys setObject:a forKey:connectionName];
} else {
@@ -1063,7 +1064,7 @@ static NSComparisonResult compareStrings(NSString *s1, NSString *s2, void* conte
NSUInteger i = 0;
for(i=0; i<[selectedItem count]-2; i++) {
NSString *item = NSArrayObjectAtIndex(selectedItem, i);
- if(![item length]) continue;
+ if([item isNSNull] || ![item length]) continue;
[infoArray addObject:[NSString stringWithFormat:@"%@: %@",
[self tableInfoLabelForIndex:i ofType:0],
[item stringByReplacingOccurrencesOfString:@"," withString:@", "]]];
diff --git a/Source/SPTablesList.m b/Source/SPTablesList.m
index 09cd8cde..77bd1541 100644
--- a/Source/SPTablesList.m
+++ b/Source/SPTablesList.m
@@ -341,10 +341,10 @@ static NSString *SPDuplicateTable = @"SPDuplicateTable";
// Query the structure of all databases in the background
if (sender == self)
// Invoked by SP
- [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:nil];
+ [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:[tableDocumentInstance databaseStructureRetrieval] withObject:nil];
else
// User press refresh button ergo force update
- [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", [NSNumber numberWithBool:YES], @"cancelQuerying", nil]];
+ [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:[tableDocumentInstance databaseStructureRetrieval] withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", [NSNumber numberWithBool:YES], @"cancelQuerying", nil]];
}
/**
diff --git a/Source/SPTextView.m b/Source/SPTextView.m
index b3db1b30..73e45c88 100644
--- a/Source/SPTextView.m
+++ b/Source/SPTextView.m
@@ -37,6 +37,7 @@
#import "SPDatabaseViewController.h"
#import "SPAppController.h"
#import "SPMySQL.h"
+#import "SPDatabaseStructure.h"
#pragma mark -
#pragma mark lex init
@@ -825,7 +826,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
NSString *theDb = (dbName == nil) ? [NSString stringWithString:currentDb] : [NSString stringWithString:dbName];
NSString *connectionID = [tableDocumentInstance connectionID];
NSString *conID = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, theDb];
- NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
+ NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[[tableDocumentInstance databaseStructureRetrieval] structure] objectForKey:connectionID]];
if(theDb && dbs != nil && [dbs count] && [dbs objectForKey:conID] && [[dbs objectForKey:conID] isKindOfClass:[NSDictionary class]]) {
NSArray *allTables = [[dbs objectForKey:conID] allKeys];
// Check if found table name is known, if not parse for aliases
@@ -887,7 +888,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
autoComplete:autoCompleteMode
oneColumn:isDictMode
alias:alias
- isQueryingDBStructure:[mySQLConnection isQueryingDatabaseStructure]];
+ withDBStructureRetriever:[tableDocumentInstance databaseStructureRetrieval]];
completionParseRangeLocation = parseRange.location;
@@ -1452,7 +1453,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
if (tablesListInstance && [tablesListInstance tableName])
currentTable = [tablesListInstance tableName];
- NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
+ NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[[tableDocumentInstance databaseStructureRetrieval] structure] objectForKey:connectionID]];
if(currentDb != nil && currentTable != nil && dbs != nil && [dbs count] && [dbs objectForKey:currentDb] && [[dbs objectForKey:currentDb] objectForKey:currentTable]) {
NSDictionary * theTable = [[dbs objectForKey:currentDb] objectForKey:currentTable];
NSArray *allFields = [theTable allKeys];
@@ -1524,7 +1525,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
autoComplete:NO
oneColumn:NO
alias:nil
- isQueryingDBStructure:NO];
+ withDBStructureRetriever:nil];
//Get the NSPoint of the first character of the current word
NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,1) actualCharacterRange:NULL];
@@ -1681,7 +1682,7 @@ NSInteger _alphabeticSort(id string1, id string2, void *reverse)
autoComplete:NO
oneColumn:YES
alias:nil
- isQueryingDBStructure:NO];
+ withDBStructureRetriever:nil];
//Get the NSPoint of the first character of the current word
NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,1) actualCharacterRange:NULL];
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 62945ff0..9e6e2389 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -209,6 +209,8 @@
5847577D120A1E8A0057631F /* Sequel Pro.qlgenerator in Copy QuickLook Plugins */ = {isa = PBXBuildFile; fileRef = 584754C2120A04560057631F /* Sequel Pro.qlgenerator */; };
584D804A15056AC200F24774 /* SPMySQL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 584D804315056AA700F24774 /* SPMySQL.framework */; };
584D804D15056ACB00F24774 /* SPMySQL.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 584D804315056AA700F24774 /* SPMySQL.framework */; };
+ 584D810D1505784100F24774 /* SPObjectAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D810C1505784100F24774 /* SPObjectAdditions.m */; };
+ 584D81AC1505922600F24774 /* SPDatabaseStructure.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D81AB1505922600F24774 /* SPDatabaseStructure.m */; };
584F5F8F0F50ACD800036517 /* table-view-small.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 584F5F8E0F50ACD800036517 /* table-view-small.tiff */; };
586AAB1514FAD3AF007F82BF /* QueryKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17E5955314F304000054EE08 /* QueryKit.framework */; };
586AAB9314FAD40D007F82BF /* QueryKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 17E5955314F304000054EE08 /* QueryKit.framework */; };
@@ -269,7 +271,6 @@
58C3506710B9A57300D37E14 /* button_right.png in Resources */ = {isa = PBXBuildFile; fileRef = 58C3506610B9A57300D37E14 /* button_right.png */; };
58C3506B10B9AA8B00D37E14 /* button_pagination.png in Resources */ = {isa = PBXBuildFile; fileRef = 58C3506A10B9AA8B00D37E14 /* button_pagination.png */; };
58C3507510B9ADEA00D37E14 /* ContentPaginationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58C3507310B9ADEA00D37E14 /* ContentPaginationView.xib */; };
- 58C458DF10CF188F00E6E13E /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 17B7B58F1016028F00F057DE /* libcrypto.dylib */; };
58C4593810D0674D00E6E13E /* spficon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 58C4593610D0674D00E6E13E /* spficon.icns */; };
58C4593910D0674E00E6E13E /* sqlicon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 58C4593710D0674D00E6E13E /* sqlicon.icns */; };
58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C56EF40F438E120035701E /* SPDataCellFormatter.m */; };
@@ -916,6 +917,10 @@
58475708120A1B630057631F /* German */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = German; path = German.lproj/SPQLPluginQueryFavoritesTemplate.html; sourceTree = "<group>"; };
58475709120A1B630057631F /* German */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = German; path = German.lproj/SPQLPluginSQLTemplate.html; sourceTree = "<group>"; };
584D803E15056AA700F24774 /* SPMySQLFramework.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SPMySQLFramework.xcodeproj; path = Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj; sourceTree = "<group>"; };
+ 584D810B1505784100F24774 /* SPObjectAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPObjectAdditions.h; sourceTree = "<group>"; };
+ 584D810C1505784100F24774 /* SPObjectAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPObjectAdditions.m; sourceTree = "<group>"; };
+ 584D81AA1505922600F24774 /* SPDatabaseStructure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDatabaseStructure.h; sourceTree = "<group>"; };
+ 584D81AB1505922600F24774 /* SPDatabaseStructure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDatabaseStructure.m; sourceTree = "<group>"; };
584F5F8E0F50ACD800036517 /* table-view-small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "table-view-small.tiff"; sourceTree = "<group>"; };
586EBD2311418D7C00B3DE45 /* FeedbackReporter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FeedbackReporter.framework; path = Frameworks/FeedbackReporter.framework; sourceTree = "<group>"; };
586F432A0FD74CFC00B428D7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/SSHQuestionDialog.xib; sourceTree = "<group>"; };
@@ -1296,7 +1301,6 @@
584D804A15056AC200F24774 /* SPMySQL.framework in Frameworks */,
296DC89F0F8FD336002A3258 /* WebKit.framework in Frameworks */,
58B907CA11BDA541000826E5 /* PSMTabBar.framework in Frameworks */,
- 58C458DF10CF188F00E6E13E /* libcrypto.dylib in Frameworks */,
B52ECDDC10DDACE9009DC3E8 /* BWToolkitFramework.framework in Frameworks */,
586EBD2411418D7C00B3DE45 /* FeedbackReporter.framework in Frameworks */,
179ECECA11F265FC009C6A40 /* libbz2.dylib in Frameworks */,
@@ -1569,6 +1573,8 @@
17D3C66D128AD4710047709F /* SPFavoritesController.m */,
17148563125F5FF500321285 /* SPDatabaseCharacterSets.h */,
17148564125F5FF500321285 /* SPDatabaseCharacterSets.m */,
+ 584D81AA1505922600F24774 /* SPDatabaseStructure.h */,
+ 584D81AB1505922600F24774 /* SPDatabaseStructure.m */,
);
name = "Data Controllers";
sourceTree = "<group>";
@@ -2483,6 +2489,8 @@
1789343B0F30C1DD0097539A /* SPStringAdditions.m */,
58DC10D112A1B8DF00B76DA5 /* SPMenuAdditions.h */,
58DC10D212A1B8DF00B76DA5 /* SPMenuAdditions.m */,
+ 584D810B1505784100F24774 /* SPObjectAdditions.h */,
+ 584D810C1505784100F24774 /* SPObjectAdditions.m */,
B52460D50F8EF92300171639 /* SPTextViewAdditions.h */,
B52460D60F8EF92300171639 /* SPTextViewAdditions.m */,
B57747D70F7A8990003B34F9 /* SPWindowAdditions.h */,
@@ -3185,6 +3193,8 @@
1713C740140D8AEF00CFD461 /* SPQueryDocumentsController.m in Sources */,
1713C75F140D8D5900CFD461 /* SPQueryConsoleDataSource.m in Sources */,
17902612141025BB005F677F /* SPQueryControllerInitializer.m in Sources */,
+ 584D810D1505784100F24774 /* SPObjectAdditions.m in Sources */,
+ 584D81AC1505922600F24774 /* SPDatabaseStructure.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};