aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2009-02-18 21:47:29 +0000
committerrowanbeentje <rowan@beent.je>2009-02-18 21:47:29 +0000
commit3ca168863ddbdc3ac3bba0cfd9a64ce262cbfea8 (patch)
tree19639d2095efd7c3d48ac2dedf893653f8a60c2e
parent2525366dbfed3aef78beaed89630ea543389cec1 (diff)
downloadsequelpro-3ca168863ddbdc3ac3bba0cfd9a64ce262cbfea8.tar.gz
sequelpro-3ca168863ddbdc3ac3bba0cfd9a64ce262cbfea8.tar.bz2
sequelpro-3ca168863ddbdc3ac3bba0cfd9a64ce262cbfea8.zip
Sets and enforces a connection timeout, and handles connection timeouts appropriately - offering to retry, reconnect, or disconnect. This fixes Issue #93, Issue #69, and Issue #77.
The gory details: Previously, MCPKit was correctly running mysql_ping to ensure a connection still existed before running a query, and aborted the query if the connection was no longer active.However the code very rarely checked the response of this, so if a query failed subsequent queries would continue to be run and the program would end up checking non-existent results, throwing Cocoa exceptions and generally breaking. However, mysql_ping would also use the default timeout (30 seconds) for each check - when running the (previous to r333) 14 queries to switch tables, this resulted in a long hang before the program even broke. To exacerbate the issue, certain situations triggered a bug present in mysql_ping in the old client binaries we're using (http://bugs.mysql.com/bug.php?id=9678), causing mysql_ping to never return despite the presence of a timeout, and so causing an indefinite hang. This issue has been fixed by: - Setting a new 10 second connection timeout for both new connections (Issue #69) and for mysql_pings. Once preferences have been redesigned we'll probably make this value editable. - Enforce the 10 second timeout even if mysql_ping hangs by using interrupts. - Wrap mysql_ping in a new method to do the above and also catch re-established connections without reporting false failures. - When a connection has failed, prompt the user to Retry, Reconnect, or Disconnect. Reconnect uses the original details for the old connection to establish a new connection, also attempting to preserve the current encoding. - Do not return control to the main loop until a connection has been reestablished (or disconnected) - this ensures the program is never in a broken state without having to rewrite all query usage. Much of the above patches the MCPKit connection methods as necessary.
-rw-r--r--Interfaces/English.lproj/ConnectionErrorDialog.xib484
-rw-r--r--Source/CMMCPConnection.h31
-rw-r--r--Source/CMMCPConnection.m330
-rw-r--r--Source/TableDocument.m2
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj13
5 files changed, 839 insertions, 21 deletions
diff --git a/Interfaces/English.lproj/ConnectionErrorDialog.xib b/Interfaces/English.lproj/ConnectionErrorDialog.xib
new file mode 100644
index 00000000..f448beb9
--- /dev/null
+++ b/Interfaces/English.lproj/ConnectionErrorDialog.xib
@@ -0,0 +1,484 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.InterfaceBuilderVersion">677</string>
+ <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">NSObject</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1018659152">
+ <int key="NSWindowStyleMask">1</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 355}, {533, 155}}</string>
+ <int key="NSWTFlags">603979776</int>
+ <string key="NSWindowTitle">Connection Error</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="552671949">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="625523505">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrame">{{112, 101}, {404, 34}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="1045633636">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">272629760</int>
+ <string key="NSContents">Sequel Pro appears to have lost the connection to the server, or the server has stopped responding.</string>
+ <object class="NSFont" key="NSSupport" id="874654698">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="625523505"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSButton" id="415418827">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{423, 13}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="706644831">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Retry</string>
+ <reference key="NSSupport" ref="874654698"/>
+ <reference key="NSControlView" ref="415418827"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="725257225">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{318, 12}, {105, 32}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <int key="NSTag">1</int>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="884446740">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Reconnect</string>
+ <reference key="NSSupport" ref="874654698"/>
+ <reference key="NSControlView" ref="725257225"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="946689239">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{170, 13}, {148, 32}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <int key="NSTag">2</int>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="47055839">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Close connection</string>
+ <reference key="NSSupport" ref="874654698"/>
+ <reference key="NSControlView" ref="946689239"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSImageView" id="974501507">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{20, 61}, {75, 74}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="24661938">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <object class="NSCustomResource" key="NSContents">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">appicon</string>
+ </object>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">0</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">YES</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ <object class="NSImageView" id="987221964">
+ <reference key="NSNextResponder" ref="552671949"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{70, 57}, {32, 32}}</string>
+ <reference key="NSSuperview" ref="552671949"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="572830162">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <object class="NSCustomResource" key="NSContents">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSNetwork</string>
+ </object>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">0</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">YES</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ </object>
+ <string key="NSFrameSize">{533, 155}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">connectionErrorDialog</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1018659152"/>
+ </object>
+ <int key="connectionID">21</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">closeSheet:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="706644831"/>
+ </object>
+ <int key="connectionID">30</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">closeSheet:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="725257225"/>
+ </object>
+ <int key="connectionID">31</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">closeSheet:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="946689239"/>
+ </object>
+ <int key="connectionID">32</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1002">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="1002"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1018659152"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="552671949"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">ConnectionErrorDialog</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="552671949"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="974501507"/>
+ <reference ref="987221964"/>
+ <reference ref="415418827"/>
+ <reference ref="625523505"/>
+ <reference ref="725257225"/>
+ <reference ref="946689239"/>
+ </object>
+ <reference key="parent" ref="1018659152"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="625523505"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1045633636"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="1045633636"/>
+ <reference key="parent" ref="625523505"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="415418827"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="706644831"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="706644831"/>
+ <reference key="parent" ref="415418827"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="725257225"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="884446740"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="884446740"/>
+ <reference key="parent" ref="725257225"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="946689239"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="47055839"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="47055839"/>
+ <reference key="parent" ref="946689239"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">26</int>
+ <reference key="object" ref="974501507"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="24661938"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">27</int>
+ <reference key="object" ref="24661938"/>
+ <reference key="parent" ref="974501507"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">28</int>
+ <reference key="object" ref="987221964"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="572830162"/>
+ </object>
+ <reference key="parent" ref="552671949"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="572830162"/>
+ <reference key="parent" ref="987221964"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>14.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>8.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>{{189, 637}, {533, 155}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{189, 637}, {533, 155}}</string>
+ <boolean value="NO"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">32</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">closeSheet:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">connectionErrorDialog</string>
+ <string key="NS.object.0">NSWindow</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <string key="minorKey"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../sequel-pro.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/Source/CMMCPConnection.h b/Source/CMMCPConnection.h
index 7b379d01..3a6b4016 100644
--- a/Source/CMMCPConnection.h
+++ b/Source/CMMCPConnection.h
@@ -26,6 +26,11 @@
#import <MCPKit_bundled/MCPKit_bundled.h>
#import "CMMCPResult.h"
+// Set the connection timeout to enforce for all connections - used for the initial connection
+// timeout and ping timeouts, but not for long queries/reads/writes.
+// Probably worth moving this to a preference at some point.
+#define SP_CONNECTION_TIMEOUT 10
+
@interface NSObject (CMMCPConnectionDelegate)
- (void)willQueryString:(NSString *)query;
@@ -34,11 +39,31 @@
@end
@interface CMMCPConnection : MCPConnection {
+ IBOutlet NSWindow *connectionErrorDialog;
+ NSWindow *parentWindow;
id delegate;
+
+ BOOL nibLoaded;
+ NSString *connectionLogin;
+ NSString *connectionPassword;
+ NSString *connectionHost;
+ int connectionPort;
+ NSString *connectionSocket;
}
-- (CMMCPResult *)queryString:(NSString *) query;
-- (void)setDelegate:(id)object;
-- (NSTimeZone *)timeZone;
+- (id) init;
+- (id) initToHost:(NSString *) host withLogin:(NSString *) login password:(NSString *) pass usingPort:(int) port;
+- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login password:(NSString *) pass;
+- (void) initSPExtensions;
+- (BOOL) connectWithLogin:(NSString *) login password:(NSString *) pass host:(NSString *) host port:(int) port socket:(NSString *) socket;
+- (void) disconnect;
+- (BOOL) reconnect;
+- (IBAction) closeSheet:(id)sender;
+- (void) setParentWindow:(NSWindow *)theWindow;
+- (CMMCPResult *) queryString:(NSString *) query;
+- (BOOL) checkConnection;
+- (void) setDelegate:(id)object;
+- (NSTimeZone *) timeZone;
+- (BOOL) pingConnection;
@end
diff --git a/Source/CMMCPConnection.m b/Source/CMMCPConnection.m
index 8d4e2673..20f5ba11 100644
--- a/Source/CMMCPConnection.m
+++ b/Source/CMMCPConnection.m
@@ -23,10 +23,191 @@
// Or mail to <lorenz@textor.ch>
#import "CMMCPConnection.h"
+#include <unistd.h>
+#include <setjmp.h>
+static jmp_buf pingTimeoutJumpLocation;
+static void forcePingTimeout(int signalNumber);
@implementation CMMCPConnection
+
+/*
+ * Override the normal init methods, extending them to also init additional details.
+ */
+- (id) init
+{
+ [self initSPExtensions];
+ self = [super init];
+ return self;
+}
+- (id) initToHost:(NSString *) host withLogin:(NSString *) login password:(NSString *) pass usingPort:(int) port
+{
+ [self initSPExtensions];
+ self = [super initToHost:host withLogin:login password:pass usingPort:port];
+ return self;
+}
+- (id) initToSocket:(NSString *) socket withLogin:(NSString *) login password:(NSString *) pass
+{
+ [self initSPExtensions];
+ self = [super initToSocket:socket withLogin:login password:pass];
+ return self;
+}
+
+
+/*
+ * Instantiate extra variables and load the connection error dialog for potential use.
+ */
+- (void) initSPExtensions
+{
+ parentWindow = nil;
+ connectionLogin = nil;
+ connectionPassword = nil;
+ connectionHost = nil;
+ connectionPort = 0;
+ connectionSocket = nil;
+ [NSBundle loadNibNamed:@"ConnectionErrorDialog" owner:self];
+}
+
+
+/*
+ * Override the normal connection method, extending it to also store details of the
+ * current connection to allow reconnection as necessary. This also sets the connection timeout
+ * - used for pings, not for long-running commands.
+ */
+- (BOOL) connectWithLogin:(NSString *) login password:(NSString *) pass host:(NSString *) host port:(int) port socket:(NSString *) socket
+{
+ if (connectionLogin) [connectionLogin release];
+ if (login) connectionLogin = [[NSString alloc] initWithString:login];
+ if (connectionPassword) [connectionPassword release];
+ if (pass) connectionPassword = [[NSString alloc] initWithString:pass];
+ if (connectionHost) [connectionHost release];
+ if (host) connectionHost = [[NSString alloc] initWithString:host];
+ connectionPort = port;
+ if (connectionSocket) [connectionSocket release];
+ if (socket) connectionSocket = [[NSString alloc] initWithString:socket];
+
+ if (mConnection != NULL) {
+ unsigned int connectionTimeout = SP_CONNECTION_TIMEOUT;
+ mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+ }
+
+ return [super connectWithLogin:login password:pass host:host port:port socket:socket];
+}
+
+
+/*
+ * Override the stored disconnection method to ensure that disconnecting clears stored details.
+ */
+- (void) disconnect
+{
+ [super disconnect];
+
+ if (connectionLogin) [connectionLogin release];
+ connectionLogin = nil;
+ if (connectionPassword) [connectionPassword release];
+ connectionPassword = nil;
+ if (connectionHost) [connectionHost release];
+ connectionHost = nil;
+ connectionPort = 0;
+ if (connectionSocket) [connectionSocket release];
+ connectionSocket = nil;
+}
+
+
+/*
+ * Reconnect to the currently "active" - but possibly disconnected - connection, using the
+ * stored details.
+ * Error checks extensively - if this method fails, it will ask how to proceed and loop depending
+ * on the status, not returning control until either a connection has been established or
+ * the connection and document have been closed.
+ */
+- (BOOL) reconnect
+{
+ NSString *currentEncoding = nil;
+ NSString *currentDatabase = nil;
+
+ // Store the current database and encoding so they can be re-set if reconnection was successful
+ if (delegate && [delegate valueForKey:@"selectedDatabase"]) {
+ currentDatabase = [NSString stringWithString:[delegate valueForKey:@"selectedDatabase"]];
+ }
+ if (delegate && [delegate valueForKey:@"_encoding"]) {
+ currentEncoding = [NSString stringWithString:[delegate valueForKey:@"_encoding"]];
+ }
+
+ // Close the connection if it exists.
+ if (mConnected) {
+ mysql_close(mConnection);
+ mConnection = NULL;
+ }
+ mConnected = NO;
+
+ // Attempt to reinitialise the connection - if this fails, it will still be set to NULL.
+ if (mConnection == NULL) {
+ mConnection = mysql_init(NULL);
+ }
+
+ if (mConnection != NULL) {
+
+ // Set a connection timeout for the new connection
+ unsigned int connectionTimeout = SP_CONNECTION_TIMEOUT;
+ mysql_options(mConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+
+ // Attempt to reestablish the connection - using own method so everything gets set up as standard.
+ // Will store the supplied details again, which isn't a problem.
+ [self connectWithLogin:connectionLogin password:connectionPassword host:connectionHost port:connectionPort socket:connectionSocket];
+ }
+
+ // If the connection was successfully established, reselect the old database and encoding if appropriate.
+ if (mConnected) {
+ if (currentDatabase) {
+ [self selectDB:currentDatabase];
+ }
+ if (currentEncoding) {
+ [self queryString:[NSString stringWithFormat:@"SET NAMES '%@'", currentEncoding]];
+ [self setEncoding:[CMMCPConnection encodingForMySQLEncoding:[currentEncoding UTF8String]]];
+ }
+ } else if (parentWindow) {
+
+ // If the connection was not successfully established, ask how to proceed.
+ [NSApp beginSheet:connectionErrorDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ int connectionErrorCode = [NSApp runModalForWindow:connectionErrorDialog];
+ [NSApp endSheet:connectionErrorDialog];
+ [connectionErrorDialog orderOut:nil];
+
+ switch (connectionErrorCode) {
+
+ // Should disconnect
+ case 2:
+ [parentWindow close];
+ return NO;
+
+ // Should retry
+ default:
+ return [self reconnect];
+ }
+ }
+
+ return mConnected;
+}
+
+
+/*
+ * Set the parent window of the connection for use with dialogs.
+ */
+- (void)setParentWindow:(NSWindow *)theWindow {
+ parentWindow = theWindow;
+}
+
+
+/*
+ * Ends and existing modal session
+ */
+- (IBAction) closeSheet:(id)sender
+{
+ [NSApp stopModalWithCode:[sender tag]];
+}
+
/*
Gets a proper NSStringEncoding according to the given MySQL charset.
@@ -92,7 +273,7 @@ WARNING : incomplete implementation. Please, send your fixes.
if (!strncmp(mysqlEncoding, "sjis", 4)) {
return NSShiftJISStringEncoding;
}
-
+
// default to iso latin 1, even if it is not exact (throw an exception?)
NSLog(@"warning: unknown encoding %s! falling back to latin1.", mysqlEncoding);
return NSISOLatin1StringEncoding;
@@ -101,7 +282,10 @@ WARNING : incomplete implementation. Please, send your fixes.
/*
- modified version of queryString to be used in sequel-pro
+ * Modified version of queryString to be used in Sequel Pro.
+ * Error checks extensively - if this method fails, it will ask how to proceed and loop depending
+ * on the status, not returning control until either the query has been executed and the result can
+ * be returned or the connection and document have been closed.
*/
- (CMMCPResult *)queryString:(NSString *) query
{
@@ -109,30 +293,29 @@ WARNING : incomplete implementation. Please, send your fixes.
const char *theCQuery = [self cStringFromString:query];
int theQueryCode;
- // check connection
- if (![self checkConnection]) {
- NSLog(@"Connection was gone, but should be reestablished now!");
- }
+ // If no connection is present, return nil.
+ if (!mConnected) return nil;
+
+ // Check the connection. This triggers reconnects as necessary, and should only return false if a disconnection
+ // has been requested - in which case return nil
+ if (![self checkConnection]) return nil;
- // inform the delegate about the query
+ // Inform the delegate about the query
if (delegate && [delegate respondsToSelector:@selector(willQueryString:)]) {
[delegate willQueryString:query];
}
if (0 == (theQueryCode = mysql_query(mConnection, theCQuery))) {
if (mysql_field_count(mConnection) != 0) {
- // use CMMCPResult instad of MCPResult
+
+ // Use CMMCPResult instad of MCPResult
theResult = [[CMMCPResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone];
} else {
return nil;
}
} else {
-// NSLog (@"Problem in queryString error code is : %d, query is : %s -in ObjC : %@-\n", theQueryCode, theCQuery, query);
-// NSLog(@"Error message is : %@\n", [self getLastErrorMessage]);
-// theResult = [theResult init]; // Old version...
-// theResult = nil;
-
- // inform the delegate about errors
+
+ // Inform the delegate about errors
if (delegate && [delegate respondsToSelector:@selector(queryGaveError:)]) {
[delegate queryGaveError:[self getLastErrorMessage]];
}
@@ -142,6 +325,50 @@ WARNING : incomplete implementation. Please, send your fixes.
return [theResult autorelease];
}
+static void sigalarm(int segnum) {
+ NSLog(@"BOOOOYAAAAA");
+}
+/*
+ * Checks whether the connection to the server is still active. If not, prompts for what approach to take,
+ * offering to retry, reconnect or disconnect the connection.
+ */
+- (BOOL)checkConnection
+{
+ if (!mConnected) return NO;
+
+ BOOL connectionVerified = FALSE;
+
+ // Check whether the connection is still operational via a wrapped version of MySQL ping.
+ connectionVerified = [self pingConnection];
+
+ // If the connection doesn't appear to be responding, show a dialog asking how to proceed
+ if (!connectionVerified) {
+ [NSApp beginSheet:connectionErrorDialog modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ int responseCode = [NSApp runModalForWindow:connectionErrorDialog];
+ [NSApp endSheet:connectionErrorDialog];
+ [connectionErrorDialog orderOut:nil];
+
+ switch (responseCode) {
+
+ // "Reconnect" has been selected. Request a reconnect, and retry.
+ case 1:
+ [self reconnect];
+ return [self checkConnection];
+
+ // "Disconnect" has been selected. Close the parent window, which will handle disconnections, and return false.
+ case 2:
+ [parentWindow close];
+ return FALSE;
+
+ // "Retry" has been selected - return a recursive call.
+ default:
+ return [self checkConnection];
+ }
+ }
+
+ return connectionVerified;
+}
+
- (void)setDelegate:(id)object
{
delegate = object;
@@ -156,7 +383,7 @@ WARNING : incomplete implementation. Please, send your fixes.
NSArray *theRow;
id theTZName;
NSTimeZone *theTZ;
-
+
[theSessionTZ dataSeek:1ULL];
theRow = [theSessionTZ fetchRowAsArray];
theTZName = [theRow objectAtIndex:1];
@@ -176,7 +403,7 @@ WARNING : incomplete implementation. Please, send your fixes.
theTZName = [self stringWithText:theTZName];
}
}
-
+
if (theTZName) { // Old versions of the server does not support there own time zone ?
theTZ = [NSTimeZone timeZoneWithName:theTZName];
} else {
@@ -195,7 +422,7 @@ WARNING : incomplete implementation. Please, send your fixes.
NSLog(@"The time zone is not defined on the server, set it to the default one : %@", theTZ);
}
}
-
+
if (theTZ != mTimeZone) {
[mTimeZone release];
mTimeZone = [theTZ retain];
@@ -204,4 +431,71 @@ WARNING : incomplete implementation. Please, send your fixes.
return mTimeZone;
}
-@end
+
+/*
+ * The current versions of MCPKit (and up to and including 3.0.1) use MySQL 4.1.12; this has an issue with
+ * mysql_ping where a connection which is terminated will cause mysql_ping never to respond, even when
+ * connection timeouts are set. Full details of this issue are available at http://bugs.mysql.com/bug.php?id=9678 ;
+ * this bug was fixed in 4.1.22 and later versions.
+ * This issue can be replicated by connecting to a remote host, and then configuring a firewall on that host
+ * to drop all packets on the connected port - mysql_ping and so Sequel Pro will hang.
+ * Until the client libraries are updated, this provides a drop-in wrapper for mysql_ping, which calls mysql_ping
+ * while running a SIGALRM to enforce the specified connection time. This is low-level but effective.
+ * Unlike mysql_ping, this function returns FALSE on failure and TRUE on success.
+ */
+- (BOOL) pingConnection
+{
+ struct sigaction timeoutAction;
+ NSDate *startDate = [NSDate date];
+ BOOL pingSuccess = FALSE;
+
+ // Construct the SIGALRM to fire after the connection timeout if it isn't cleared, calling the forcePingTimeout function.
+ timeoutAction.sa_handler = forcePingTimeout;
+ sigemptyset(&timeoutAction.sa_mask);
+ timeoutAction.sa_flags = 0;
+ sigaction(SIGALRM, &timeoutAction, NULL);
+ alarm(SP_CONNECTION_TIMEOUT+1);
+
+ // Set up a "restore point", returning 0; if longjmp is used later with this reference, execution
+ // jumps back to this point and returns a nonzero value, so this function evaluates to false when initially
+ // set and true if it's called again.
+ if (setjmp(pingTimeoutJumpLocation)) {
+
+ // The connection timed out - we want to return false.
+ pingSuccess = FALSE;
+
+ // On direct execution:
+ } else {
+
+ // Run mysql_ping, which returns 0 on success, and otherwise an error.
+ pingSuccess = (BOOL)(! mysql_ping(mConnection));
+
+ // If the ping failed within a second, try another one; this is because a terminated-but-then
+ // restored connection is at times restored or functional after a ping, but the ping still returns
+ // an error. This additional check ensures the returned status is correct with minimal other effect.
+ if (!pingSuccess && ([[NSDate date] timeIntervalSinceDate:startDate] < 1)) {
+ pingSuccess = (BOOL)(! mysql_ping(mConnection));
+ }
+ }
+
+ // Reset and clear the SIGALRM used to check connection timeouts.
+ alarm(0);
+ timeoutAction.sa_handler = SIG_IGN;
+ sigemptyset(&timeoutAction.sa_mask);
+ timeoutAction.sa_flags = 0;
+ sigaction(SIGALRM, &timeoutAction, NULL);
+
+
+ return pingSuccess;
+}
+
+/*
+ * This function is paired with pingConnection, and provides a method of enforcing the connection
+ * timeout when mysql_ping does not respect the specified limits.
+ */
+static void forcePingTimeout(int signalNumber)
+{
+ longjmp(pingTimeoutJumpLocation, 1);
+}
+
+@end \ No newline at end of file
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index b9cb66a6..10ef7dca 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -158,6 +158,8 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa
password:[passwordField stringValue]
usingPort:[portField intValue]];
}
+ [mySQLConnection setParentWindow:tableWindow];
+
if ( ![mySQLConnection isConnected] )
code = 2;
if ( !code && ![[databaseField stringValue] isEqualToString:@""] ) {
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 0c4f7839..52a315eb 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -93,6 +93,7 @@
4DECC48F0EC2B436008D359E /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3320EC2A170008D359E /* Sparkle.framework */; };
4DECC4900EC2B436008D359E /* MCPKit_bundled.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3330EC2A170008D359E /* MCPKit_bundled.framework */; };
4DECC4910EC2B436008D359E /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3340EC2A170008D359E /* Growl.framework */; };
+ 58186D210F4CB38900851FE9 /* ConnectionErrorDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */; };
58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C56EF40F438E120035701E /* SPDataCellFormatter.m */; };
58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; };
58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; };
@@ -248,6 +249,7 @@
4DECC3320EC2A170008D359E /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Sparkle.framework; sourceTree = "<group>"; };
4DECC3330EC2A170008D359E /* MCPKit_bundled.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MCPKit_bundled.framework; path = Frameworks/MCPKit_bundled.framework; sourceTree = "<group>"; };
4DECC3340EC2A170008D359E /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; sourceTree = "<group>"; };
+ 58186D200F4CB38900851FE9 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Interfaces/English.lproj/ConnectionErrorDialog.xib; sourceTree = "<group>"; };
58C56EF30F438E120035701E /* SPDataCellFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDataCellFormatter.h; sourceTree = "<group>"; };
58C56EF40F438E120035701E /* SPDataCellFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataCellFormatter.m; sourceTree = "<group>"; };
58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = "<group>"; };
@@ -540,6 +542,7 @@
children = (
17E642060EF020CB001BC333 /* DBView.xib */,
1761FD460EF03A6F00331368 /* MainMenu.xib */,
+ 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */,
);
path = Interfaces;
sourceTree = "<group>";
@@ -688,6 +691,7 @@
17E6423E0EF0218B001BC333 /* InfoPlist.strings in Resources */,
1761FD480EF03A6F00331368 /* MainMenu.xib in Resources */,
B5E2C5FA0F2353B5007446E0 /* TablePropertyIcon.png in Resources */,
+ 58186D210F4CB38900851FE9 /* ConnectionErrorDialog.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -777,6 +781,15 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
+ 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 58186D200F4CB38900851FE9 /* English */,
+ );
+ name = ConnectionErrorDialog.xib;
+ path = ..;
+ sourceTree = "<group>";
+ };
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */