aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2011-02-11 02:06:18 +0000
committerrowanbeentje <rowan@beent.je>2011-02-11 02:06:18 +0000
commite3ac547a6fb5b73581e61bb35ac2f3f7fca67a96 (patch)
tree8ed159102057d9a26a5dd13e303cf1ce37da7c3b
parentf5a99e49839174bdfa2364fbba1c963da4a3481b (diff)
downloadsequelpro-e3ac547a6fb5b73581e61bb35ac2f3f7fca67a96.tar.gz
sequelpro-e3ac547a6fb5b73581e61bb35ac2f3f7fca67a96.tar.bz2
sequelpro-e3ac547a6fb5b73581e61bb35ac2f3f7fca67a96.zip
- Revert r3187, which only masked an underlying bug and caused double columns in views
- In SPTableStructureDelete, don't request the encoding if the table is reloading; this prevents multithreaded data fetches and race conditions causing Issue #974. - Alter SPTableData to use thread mutexes instead of a boolean to prevent threading issues, which also fixes Issue #974 in a different way. Alter race condition checks to block their threads instead of returning bogus information to prevent state issues. - Alter table loading to no longer load trigger information until required, speeding up use of other views and cleaning up the console log.
-rw-r--r--Source/SPDatabaseViewController.m25
-rw-r--r--Source/SPTableData.h4
-rw-r--r--Source/SPTableData.m110
-rw-r--r--Source/SPTableStructureDelegate.m5
-rw-r--r--Source/SPTableTriggers.h1
-rw-r--r--Source/SPTableTriggers.m32
6 files changed, 90 insertions, 87 deletions
diff --git a/Source/SPDatabaseViewController.m b/Source/SPDatabaseViewController.m
index 03153230..a6600d3f 100644
--- a/Source/SPDatabaseViewController.m
+++ b/Source/SPDatabaseViewController.m
@@ -280,7 +280,7 @@
[tableSourceInstance loadTable:nil];
[tableContentInstance loadTable:nil];
[[extendedTableInfoInstance onMainThread] loadTable:nil];
- [[tableTriggersInstance onMainThread] loadTriggers];
+ [[tableTriggersInstance onMainThread] resetInterface];
structureLoaded = NO;
contentLoaded = NO;
statusLoaded = NO;
@@ -393,8 +393,12 @@
// Update the window title
[self updateWindowTitle:self];
- // Reset table information caches
+ // Reset table information caches and mark that all loaded views require their data reloading
[tableDataInstance resetAllData];
+ structureLoaded = NO;
+ contentLoaded = NO;
+ statusLoaded = NO;
+ triggersLoaded = NO;
// Ensure status and details are fetched using UTF8
NSString *previousEncoding = [mySQLConnection encoding];
@@ -408,7 +412,8 @@
[tableDataInstance updateStatusInformationForCurrentTable];
// Check the current encoding against the table encoding to see whether
- // an encoding change and reset is required
+ // an encoding change and reset is required. This also caches table information on
+ // the working thread.
if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) {
// tableEncoding == nil indicates that there was an error while retrieving table data
@@ -426,24 +431,12 @@
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
- // Cache table information on the working thread
- if (selectedTableType == SPTableTypeView)
- [tableDataInstance updateInformationForCurrentView];
- else
- [tableDataInstance updateInformationForCurrentTable];
-
// Notify listeners of the table change now that the state is fully set up.
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:self];
// Restore view states as appropriate
[spHistoryControllerInstance restoreViewStates];
- // Mark that all loaded views require their data reloading
- structureLoaded = NO;
- contentLoaded = NO;
- statusLoaded = NO;
- triggersLoaded = NO;
-
// Load the currently selected view if looking at a table or view
if (tableEncoding && (selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable))
{
@@ -475,7 +468,7 @@
if (!structureLoaded) [tableSourceInstance loadTable:nil];
if (!contentLoaded) [tableContentInstance loadTable:nil];
if (!statusLoaded) [[extendedTableInfoInstance onMainThread] loadTable:nil];
- if (!triggersLoaded) [[tableTriggersInstance onMainThread] loadTriggers];
+ if (!triggersLoaded) [[tableTriggersInstance onMainThread] resetInterface];
// Update the "Show Create Syntax" window if it's already opened
// according to the selected table/view/proc/func
diff --git a/Source/SPTableData.h b/Source/SPTableData.h
index cb24d51f..4503bd03 100644
--- a/Source/SPTableData.h
+++ b/Source/SPTableData.h
@@ -41,9 +41,9 @@
MCPConnection *mySQLConnection;
- BOOL isWorking;
- BOOL tableHasAutoIncrementField;
+ pthread_mutex_t dataProcessingLock;
+ BOOL tableHasAutoIncrementField;
}
@property (readwrite, assign) BOOL isWorking;
diff --git a/Source/SPTableData.m b/Source/SPTableData.m
index 28717864..c63d10ff 100644
--- a/Source/SPTableData.m
+++ b/Source/SPTableData.m
@@ -31,11 +31,13 @@
#import "RegexKitLite.h"
#import "SPServerSupport.h"
+@interface SPTableData (PrivateAPI)
+- (void)_loopWhileWorking;
+@end
+
@implementation SPTableData
-@synthesize isWorking;
@synthesize tableHasAutoIncrementField;
-;
/**
* Init class.
@@ -52,9 +54,9 @@
tableEncoding = nil;
tableCreateSyntax = nil;
mySQLConnection = nil;
- isWorking = NO;
tableHasAutoIncrementField = NO;
+ pthread_mutex_init(&dataProcessingLock, NULL);
}
return self;
@@ -79,8 +81,8 @@
- (NSString *) tableEncoding
{
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if (tableEncoding == nil) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -98,8 +100,8 @@
- (NSString *) tableCreateSyntax
{
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if (tableCreateSyntax == nil) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -123,8 +125,8 @@
- (NSArray *) columns
{
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return [NSArray array];
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columns count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -149,9 +151,8 @@
*/
- (NSArray *) triggers
{
- // Return if CREATE SYNTAX is being parsed
- if (isWorking) return [NSArray array];
-
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
// If triggers is nil, the triggers need to be loaded - if a table is selected on MySQL >= 5.0.2
if (!triggers) {
@@ -173,10 +174,8 @@
*/
- (NSDictionary *) columnWithName:(NSString *)colName
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
-
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columns count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -196,10 +195,8 @@
*/
- (NSArray *) columnNames
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return [NSArray array];
-
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columnNames count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -219,9 +216,8 @@
*/
- (NSDictionary *) columnAtIndex:(NSInteger)index
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columns count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -242,9 +238,8 @@
- (BOOL) columnIsBlobOrText:(NSString *)colName
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return YES; // to be at the safe side
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columns count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -266,9 +261,8 @@
- (BOOL) columnIsGeometry:(NSString *)colName
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return YES; // to be at the safe side
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([columns count] == 0) {
if ([tableListInstance tableType] == SPTableTypeView) {
@@ -289,9 +283,8 @@
*/
- (NSString *) statusValueForKey:(NSString *)aKey
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([status count] == 0) {
[self updateStatusInformationForCurrentTable];
@@ -318,9 +311,8 @@
*/
- (NSDictionary *) statusValues
{
-
- // Return if CREATE SYNTAX is being parsed
- if(isWorking) return nil;
+ // If processing is already in action, wait for it to complete
+ [self _loopWhileWorking];
if ([status count] == 0) {
[self updateStatusInformationForCurrentTable];
@@ -380,7 +372,7 @@
*/
- (BOOL) updateInformationForCurrentTable
{
- isWorking = YES;
+ pthread_mutex_lock(&dataProcessingLock);
NSDictionary *tableData = nil;
NSDictionary *columnData;
@@ -396,7 +388,7 @@
}
if (tableData == nil ) {
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return FALSE;
}
@@ -412,7 +404,7 @@
}
tableEncoding = [[NSString alloc] initWithString:[tableData objectForKey:@"encoding"]];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return TRUE;
}
@@ -423,8 +415,7 @@
*/
- (BOOL) updateInformationForCurrentView
{
-
- isWorking = YES;
+ pthread_mutex_lock(&dataProcessingLock);
NSDictionary *viewData = [self informationForView:[tableListInstance tableName]];
NSDictionary *columnData;
@@ -436,7 +427,7 @@
[columns removeAllObjects];
[columnNames removeAllObjects];
[constraints removeAllObjects];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return FALSE;
}
@@ -452,7 +443,7 @@
}
tableEncoding = [[NSString alloc] initWithString:[viewData objectForKey:@"encoding"]];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return TRUE;
}
@@ -476,10 +467,6 @@
unichar quoteCharacter;
BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"];
- [columns removeAllObjects];
- [columnNames removeAllObjects];
- [constraints removeAllObjects];
-
// Catch unselected tables and return nil
if ([tableName isEqualToString:@""] || !tableName) return nil;
@@ -925,14 +912,13 @@
*/
- (BOOL)updateStatusInformationForCurrentTable
{
-
- isWorking = YES;
+ pthread_mutex_lock(&dataProcessingLock);
BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"];
// Catch unselected tables and return false
if ([[tableListInstance tableName] isEqualToString:@""] || ![tableListInstance tableName]) {
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return FALSE;
}
@@ -979,7 +965,7 @@
[mySQLConnection getLastErrorMessage]]);
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
}
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return FALSE;
}
@@ -997,7 +983,7 @@
if ([[status objectForKey:@"Engine"] isNSNull]) {
[status setDictionary:[NSDictionary dictionaryWithObjectsAndKeys:@"Error", @"Engine", [NSString stringWithFormat:NSLocalizedString(@"An error occurred retrieving table information. MySQL said: %@", @"MySQL table info retrieval error message"), [status objectForKey:@"Comment"]], @"Comment", [tableListInstance tableName], @"Name", nil]];
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return FALSE;
}
@@ -1032,7 +1018,7 @@
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return TRUE;
}
@@ -1042,8 +1028,7 @@
*/
- (BOOL) updateTriggersForCurrentTable
{
-
- isWorking = YES;
+ pthread_mutex_lock(&dataProcessingLock);
// Ensure queries are made in UTF8
BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"];
@@ -1067,7 +1052,7 @@
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
}
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return NO;
}
@@ -1079,7 +1064,7 @@
if (changeEncoding) [mySQLConnection restoreStoredEncoding];
- isWorking = NO;
+ pthread_mutex_unlock(&dataProcessingLock);
return YES;
}
@@ -1364,7 +1349,18 @@
if (tableCreateSyntax) [tableCreateSyntax release];
if (mySQLConnection) [mySQLConnection release];
+ pthread_mutex_destroy(&dataProcessingLock);
+
[super dealloc];
}
-@end
+#pragma mark -
+#pragma mark Private API
+
+- (void)_loopWhileWorking
+{
+ while (pthread_mutex_trylock(&dataProcessingLock)) usleep(10000);
+ pthread_mutex_unlock(&dataProcessingLock);
+}
+
+@end \ No newline at end of file
diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m
index 17ad817d..729091d6 100644
--- a/Source/SPTableStructureDelegate.m
+++ b/Source/SPTableStructureDelegate.m
@@ -49,7 +49,10 @@
NSInteger end = [enc length] - start - 1;
collations = [databaseDataInstance getDatabaseCollationsForEncoding:[enc substringWithRange:NSMakeRange(start, end)]];
} else {
- if([tableDataInstance tableEncoding] != nil) {
+
+ // If the structure has loaded (not still loading!) and the table encoding
+ // is set, use the appropriate collations.
+ if([tableDocumentInstance structureLoaded] && [tableDataInstance tableEncoding] != nil) {
collations = [databaseDataInstance getDatabaseCollationsForEncoding:[tableDataInstance tableEncoding]];
} else {
collations = [NSArray array];
diff --git a/Source/SPTableTriggers.h b/Source/SPTableTriggers.h
index 0dfd6f30..70eeeb2e 100644
--- a/Source/SPTableTriggers.h
+++ b/Source/SPTableTriggers.h
@@ -65,6 +65,7 @@
@property (readwrite, assign) MCPConnection *connection;
- (void)loadTriggers;
+- (void)resetInterface;
// IB action methods
- (IBAction)addTrigger:(id)sender;
diff --git a/Source/SPTableTriggers.m b/Source/SPTableTriggers.m
index 44a17549..6462a43f 100644
--- a/Source/SPTableTriggers.m
+++ b/Source/SPTableTriggers.m
@@ -111,17 +111,7 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode";
{
BOOL enableInteraction = ((![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableTriggers]) || (![tableDocumentInstance isWorking]));
- // Disable all interface elements by default
- [addTriggerButton setEnabled:NO];
- [refreshTriggersButton setEnabled:NO];
- [triggersTableView setEnabled:NO];
- [labelTextField setStringValue:@""];
-
- // Show a warning if the version of MySQL is too low to support triggers
- if (![[tableDocumentInstance serverSupport] supportsTriggers]) {
- [labelTextField setStringValue:NSLocalizedString(@"This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2", @"triggers not supported label")];
- return;
- }
+ [self resetInterface];
// If no item is selected, or the item selected is not a table, return.
if (![tablesListInstance tableName] || [tablesListInstance tableType] != SPTableTypeTable)
@@ -139,6 +129,26 @@ static const NSString *SPTriggerSQLMode = @"TriggerSQLMode";
[self _refreshTriggerDataForcingCacheRefresh:NO];
}
+/**
+ * Reset the trigger interface, as for no selected table.
+ */
+- (void)resetInterface
+{
+ [triggerData removeAllObjects];
+ [triggersTableView noteNumberOfRowsChanged];
+
+ // Disable all interface elements by default
+ [addTriggerButton setEnabled:NO];
+ [refreshTriggersButton setEnabled:NO];
+ [triggersTableView setEnabled:NO];
+ [labelTextField setStringValue:@""];
+
+ // Show a warning if the version of MySQL is too low to support triggers
+ if (![[tableDocumentInstance serverSupport] supportsTriggers]) {
+ [labelTextField setStringValue:NSLocalizedString(@"This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2", @"triggers not supported label")];
+ }
+}
+
#pragma mark -
#pragma mark IB action methods