aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h3
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m3
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m61
-rw-r--r--Resources/English.lproj/Localizable.stringsbin230192 -> 229994 bytes
-rw-r--r--Source/SPEditSheetTextView.m17
-rw-r--r--Source/SPExportFilenameUtilities.m28
-rw-r--r--Source/SPExtendedTableInfo.m1
-rw-r--r--Source/SPFieldEditorController.h3
-rw-r--r--Source/SPFieldEditorController.m17
-rw-r--r--Source/SPNarrowDownCompletion.m2
-rw-r--r--Source/SPWindow.h5
-rw-r--r--Source/SPWindow.m28
14 files changed, 137 insertions, 43 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index 5772eb72..ee7f6039 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -39,7 +39,6 @@
#import "Locking.h"
#import "Conversion.h"
-
@interface SPMySQLConnection (PrivateAPI)
- (MYSQL *)_makeRawMySQLConnectionWithEncoding:(NSString *)encodingName isMasterConnection:(BOOL)isMaster;
@@ -47,6 +46,8 @@
- (void)_updateConnectionVariables;
- (void)_restoreConnectionVariables;
- (BOOL)_checkConnectionIfNecessary;
+- (void)_validateThreadSetup;
++ (void)_removeThreadVariables:(NSNotification *)aNotification;
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
index 3ce0c0cd..3201b55f 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
@@ -187,6 +187,9 @@ void _backgroundPingTask(void *ptr)
// Set up a cleanup routine
pthread_cleanup_push(_pingThreadCleanup, pingDetails);
+ // Initialise MySQL variables and handling on this thread
+ mysql_thread_init();
+
// Set up a signal handler for SIGUSR1, to handle forced timeouts.
signal(SIGUSR1, _forceThreadExit);
@@ -209,6 +212,9 @@ void _pingThreadCleanup(void *pingDetails)
{
SPMySQLConnectionPingDetails *pingDetailsStruct = pingDetails;
*(pingDetailsStruct->keepAlivePingActivePointer) = NO;
+
+ // Clean up MySQL variables and handlers
+ mysql_thread_end();
}
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
index 5df71e96..46615cae 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
@@ -76,6 +76,9 @@
}
if (![self _checkConnectionIfNecessary]) return nil;
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Perform a lossy conversion to bytes, using NSData to do the hard work. Preserves
// nul characters correctly.
NSData *cData = [theString dataUsingEncoding:stringEncoding allowLossyConversion:YES];
@@ -229,6 +232,9 @@
return nil;
}
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Check the connection if necessary, returning nil if the query couldn't be validated
if (![self _checkConnectionIfNecessary]) return nil;
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
index f695d977..1022ccd1 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
@@ -137,6 +137,9 @@
// Lock the connection before using it
[self _lockConnection];
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Get the process list
MYSQL_RES *mysqlResult = mysql_list_processes(mySQLConnection);
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index 800157ca..d42b82e6 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -36,6 +36,9 @@
#include <pthread.h>
#include <SystemConfiguration/SCNetworkReachability.h>
+// Thread flag constant
+static pthread_key_t mySQLThreadInitFlagKey;
+static void *mySQLThreadFlag;
#pragma mark Class constants
@@ -76,6 +79,24 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
#pragma mark Initialisation and teardown
/**
+ * In the one-off class initialisation, set up MySQL as necessary
+ */
++ (void)initialize
+{
+
+ // Set up a pthread thread-specific data key to be used across all classes and threads
+ pthread_key_create(&mySQLThreadInitFlagKey, NULL);
+ mySQLThreadFlag = malloc(1);
+
+ // MySQL requires mysql_library_init() to be called before any other MySQL
+ // functions are used; although mysql_init() will call it automatically, it
+ // won't do so in a thread-safe manner, so setting it up first is safer.
+ // No arguments are required.
+ // Note that this will install MySQL's SIGPIPE handler.
+ mysql_library_init(0, NULL, NULL);
+}
+
+/**
* Initialise the SPMySQLConnection object, setting up class defaults.
*
* Typically initialisation would be followed by setting the connection details
@@ -631,6 +652,13 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
MYSQL *theConnection = mysql_init(NULL);
if (!theConnection) return NULL;
+ // Calling mysql_init will have automatically installed per-thread variables if necessary,
+ // so track their installation for removal and to avoid recreating again.
+ if (!pthread_getspecific(mySQLThreadInitFlagKey)) {
+ pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
+ [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
+ }
+
// Disable automatic reconnection, as it's handled in-framework to preserve
// options, encodings and connection state.
my_bool falseMyBool = FALSE;
@@ -835,4 +863,37 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
// Otherwise check the connection
return [self checkConnection];
}
+
+/**
+ * Ensure that the thread this method is called on has been registered for
+ * use with MySQL. MySQL requires thread-specific variables for safe
+ * execution.
+ */
+- (void)_validateThreadSetup
+{
+
+ // Check to see whether the handler has already been installed
+ if (pthread_getspecific(mySQLThreadInitFlagKey)) return;
+
+ // If not, install it
+ mysql_thread_init();
+
+ // Mark the thread to avoid multiple installs
+ pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
+
+ // Set up the notification handler to deregister it
+ [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
+}
+
+/**
+ * Remove the MySQL variables and handlers from each closing thread which
+ * has had them installed to avoid memory leaks.
+ * This is a class method for easy global tracking; it will be called on the appropriate
+ * thread automatically.
+ */
++ (void)_removeThreadVariables:(NSNotification *)aNotification
+{
+ mysql_thread_end();
+}
+
@end
diff --git a/Resources/English.lproj/Localizable.strings b/Resources/English.lproj/Localizable.strings
index 6643741c..bb958cf7 100644
--- a/Resources/English.lproj/Localizable.strings
+++ b/Resources/English.lproj/Localizable.strings
Binary files differ
diff --git a/Source/SPEditSheetTextView.m b/Source/SPEditSheetTextView.m
index b028c177..a5d110a6 100644
--- a/Source/SPEditSheetTextView.m
+++ b/Source/SPEditSheetTextView.m
@@ -343,21 +343,4 @@
[self saveChangedFontInUserDefaults];
}
-/**
- * Needed to allow Find Panel inside the textView if it runs in a sheet
- */
-- (BOOL)becomeFirstResponder
-{
- return YES;
-}
-
-/**
- * Needed to allow Find Panel inside the textView if it runs in a sheet
- */
-- (BOOL)resignFirstResponder
-{
- return YES;
-}
-
-
@end
diff --git a/Source/SPExportFilenameUtilities.m b/Source/SPExportFilenameUtilities.m
index ad5ab36b..1f37a561 100644
--- a/Source/SPExportFilenameUtilities.m
+++ b/Source/SPExportFilenameUtilities.m
@@ -65,32 +65,38 @@
BOOL isCSV = exportType == SPCSVExport;
BOOL isDot = exportType == SPDotExport;
BOOL isXML = exportType == SPXMLExport;
-
- NSString *tokens = NSLocalizedString(@"host,database,table,date,year,month,day,time", @"default custom export filename tokens");;
- NSString *tokensWithoutTable = NSLocalizedString(@"host,database,date,year,month,day,time", @"custom export filename tokens without table");
-
+
+ NSMutableArray *exportTokens = [NSMutableArray arrayWithObjects:
+ NSLocalizedString(@"host", @"export filename host token"),
+ NSLocalizedString(@"database", @"export filename database token"),
+ NSLocalizedString(@"table", @"table"),
+ NSLocalizedString(@"date", @"export filename date token"),
+ NSLocalizedString(@"year", @"export filename date token"),
+ NSLocalizedString(@"month", @"export filename date token"),
+ NSLocalizedString(@"day", @"export filename date token"),
+ NSLocalizedString(@"time", @"export filename time token"),
+ nil];
+
+ // Determine whether to remove the table from the tokens list
if (exportSource == SPQueryExport || isDot) {
- tokens = tokensWithoutTable;
+ removeTable = YES;
}
else if (isSQL || isCSV || isXML) {
for (NSArray *table in tables)
{
if ([NSArrayObjectAtIndex(table, 2) boolValue]) {
i++;
- removeTable = YES;
-
if (i == 2) break;
}
}
if (i > 1) {
removeTable = isSQL ? YES : ![exportFilePerTableCheck state];
-
- tokens = isSQL ? tokensWithoutTable : ([exportFilePerTableCheck state] ? tokens : tokensWithoutTable);
}
}
if (removeTable) {
+ [exportTokens removeObject:NSLocalizedString(@"table", @"table")];
NSArray *tokenParts = [exportCustomFilenameTokenField objectValue];
for (id token in [exportCustomFilenameTokenField objectValue])
@@ -107,8 +113,8 @@
}
}
}
-
- [exportCustomFilenameTokensField setStringValue:tokens];
+
+ [exportCustomFilenameTokensField setStringValue:[exportTokens componentsJoinedByString:@","]];
}
/**
diff --git a/Source/SPExtendedTableInfo.m b/Source/SPExtendedTableInfo.m
index 23f3c426..260c90fd 100644
--- a/Source/SPExtendedTableInfo.m
+++ b/Source/SPExtendedTableInfo.m
@@ -526,6 +526,7 @@ static NSString *SPUpdateTableTypeNewType = @"SPUpdateTableTypeNewType";
*/
- (void)confirmChangeTableTypeDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSDictionary *)contextInfo
{
+ [[alert window] orderOut:self];
if (returnCode == NSAlertDefaultReturn) {
[self _changeCurrentTableTypeFrom:[contextInfo objectForKey:SPUpdateTableTypeCurrentType]
to:[contextInfo objectForKey:SPUpdateTableTypeNewType]];
diff --git a/Source/SPFieldEditorController.h b/Source/SPFieldEditorController.h
index f45b6070..da78785b 100644
--- a/Source/SPFieldEditorController.h
+++ b/Source/SPFieldEditorController.h
@@ -22,6 +22,7 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
+@class SPWindow;
/**
* @class SPFieldEditorController SPFieldEditorController.h
@@ -45,7 +46,7 @@
IBOutlet id hexTextView;
IBOutlet id editTextScrollView;
IBOutlet id hexTextScrollView;
- IBOutlet id editSheet;
+ IBOutlet SPWindow *editSheet;
IBOutlet id editSheetCancelButton;
IBOutlet id editSheetIsNotEditableCancelButton;
IBOutlet id editSheetOkButton;
diff --git a/Source/SPFieldEditorController.m b/Source/SPFieldEditorController.m
index 3a4ee14c..f94af537 100644
--- a/Source/SPFieldEditorController.m
+++ b/Source/SPFieldEditorController.m
@@ -32,6 +32,7 @@
#import "SPTooltip.h"
#import "SPGeometryDataView.h"
#import "SPCopyTable.h"
+#import "SPWindow.h"
#include <objc/objc-runtime.h>
#import "SPCustomQuery.h"
#import "SPTableContent.h"
@@ -85,6 +86,10 @@
// Allow the user to enter cmd+return to close the edit sheet in addition to fn+return
[editSheetOkButton setKeyEquivalentModifierMask:NSCommandKeyMask];
+ // Permit the field edit sheet to become main if necessary; this allows fields within the sheet to
+ // support full interactivity, for example use of the NSFindPanel inside NSTextViews.
+ [editSheet setIsSheetWhichCanBecomeMain:YES];
+
allowUndo = NO;
selectionChanged = NO;
@@ -431,19 +436,7 @@
editSheetWillBeInitialized = NO;
[editSheetProgressBar stopAnimation:self];
-
- // The field editor sheet runs as sheet thus a NSTextView won't respond to the Find Panel
- // since the Find Panel validate its buttons against [[NSApp mainWindow] firstResponder] == NSTextView.
- // After ordering out this sheet SPCopyTable remains the first responder thus set it hard.
- // This only works in conjunction with [NSTextView becomeFirstResponder] and [NSTextView resignFirstResponder]
- // which has to return YES.
-#ifndef SP_REFACTOR
- if([[self window] firstResponder] == editTextView)
- [[NSApp mainWindow] makeFirstResponder:[[self window] firstResponder]];
-#endif
-
}
-
}
/**
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index db3f5e3a..76336069 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -862,7 +862,7 @@
break;
}
}
- else if(key == NSCarriageReturnCharacter || (key == NSTabCharacter && !triggerMode))
+ else if(key == NSCarriageReturnCharacter || key == NSEnterCharacter || key == NSRightArrowFunctionKey || (key == NSTabCharacter && !triggerMode))
{
[self completeAndInsertSnippet];
}
diff --git a/Source/SPWindow.h b/Source/SPWindow.h
index 74c4f804..9809f3d3 100644
--- a/Source/SPWindow.h
+++ b/Source/SPWindow.h
@@ -25,5 +25,10 @@
#import <Cocoa/Cocoa.h>
@interface SPWindow : NSWindow
+{
+ BOOL isSheetWhichCanBecomeMain;
+}
+
+@property (assign) BOOL isSheetWhichCanBecomeMain;
@end
diff --git a/Source/SPWindow.m b/Source/SPWindow.m
index 0285ce8a..d023b7b4 100644
--- a/Source/SPWindow.m
+++ b/Source/SPWindow.m
@@ -27,6 +27,8 @@
@implementation SPWindow
+@synthesize isSheetWhichCanBecomeMain;
+
#pragma mark -
#pragma mark Keyboard shortcut additions
@@ -116,6 +118,9 @@
[super sendEvent:theEvent];
}
+#pragma mark -
+#pragma mark Undo manager handling
+
/**
* If this window is controlled by an SPWindowController, and thus supports being asked
* for the frontmost SPTableDocument, request the undoController for that table
@@ -130,4 +135,27 @@
return [super undoManager];
}
+#pragma mark -
+#pragma mark Method overrides
+
+/**
+ * Allow sheets to become main if necessary, for example if they are acting as an
+ * editor for a window.
+ */
+- (BOOL)canBecomeMainWindow
+{
+
+ // If this window is a sheet which is permitted to become main, respond appropriately
+ if ([self isSheet] && isSheetWhichCanBecomeMain) {
+ return [self isVisible];
+ }
+
+ // Otherwise, if this window has a sheet attached which can become main, return NO.
+ if ([[self attachedSheet] isKindOfClass:[SPWindow class]] && [(SPWindow *)[self attachedSheet] isSheetWhichCanBecomeMain]) {
+ return NO;
+ }
+
+ return [super canBecomeMainWindow];
+}
+
@end