aboutsummaryrefslogtreecommitdiffstats
path: root/Source/TableSource.m
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2010-03-22 23:07:03 +0000
committerrowanbeentje <rowan@beent.je>2010-03-22 23:07:03 +0000
commit9892a96b80073686b0dd1205d4f859b10d32336f (patch)
treef4f4caaa72f5983dfc23a1f686603b61baf0374c /Source/TableSource.m
parent6728ccd128a5320256ac51c0a617f0c76b331ea7 (diff)
downloadsequelpro-9892a96b80073686b0dd1205d4f859b10d32336f.tar.gz
sequelpro-9892a96b80073686b0dd1205d4f859b10d32336f.tar.bz2
sequelpro-9892a96b80073686b0dd1205d4f859b10d32336f.zip
- Simplify table source table setup and make thread safe. This should address http://log.sequelpro.com/view/43 , http://log.sequelpro.com/view/46
- Improve keepalive timer interaction - this should address http://log.sequelpro.com/view/74 and http://log.sequelpro.com/view/71 - Further thread safety improvements to Custom Query, Table Document, and the history controller
Diffstat (limited to 'Source/TableSource.m')
-rw-r--r--Source/TableSource.m299
1 files changed, 140 insertions, 159 deletions
diff --git a/Source/TableSource.m b/Source/TableSource.m
index 5f5b0119..fa0e190d 100644
--- a/Source/TableSource.m
+++ b/Source/TableSource.m
@@ -33,6 +33,7 @@
#import "SPArrayAdditions.h"
#import "SPConstants.h"
#import "SPAlertSheets.h"
+#import "SPMainThreadTrampoline.h"
@interface TableSource (PrivateAPI)
@@ -49,71 +50,36 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
*/
- (void)loadTable:(NSString *)aTable
{
- NSEnumerator *enumerator;
- id field;
+ NSArray *theTableFields, *theTableIndexes;
+ NSMutableDictionary *theTableEnumLists = [NSMutableDictionary dictionary];
NSArray *extrasArray;
NSMutableDictionary *tempDefaultValues;
- NSEnumerator *extrasEnumerator;
- id extra;
NSInteger i;
SPSQLParser *fieldParser;
- BOOL enableInteraction = ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableStructure] || ![tableDocumentInstance isWorking];
// Check whether a save of the current row is required.
- if ( ![self saveRowOnDeselect] ) return;
-
- if (selectedTable) [selectedTable release];
- if (aTable == nil) {
- selectedTable = nil;
- } else {
- selectedTable = [[NSString alloc] initWithString:aTable];
- }
- [tableSourceView deselectAll:self];
- [indexView deselectAll:self];
-
- if ( isEditingRow )
- return;
-
- // empty variables
- [enumFields removeAllObjects];
-
- if ( [aTable isEqualToString:@""] || !aTable ) {
- [tableFields removeAllObjects];
- [indexes removeAllObjects];
- [tableSourceView reloadData];
- [indexView reloadData];
- [addFieldButton setEnabled:NO];
- [copyFieldButton setEnabled:NO];
- [removeFieldButton setEnabled:NO];
- [addIndexButton setEnabled:NO];
- [removeIndexButton setEnabled:NO];
- [editTableButton setEnabled:NO];
+ if ( ![[self onMainThread] saveRowOnDeselect] ) return;
+ // If no table is selected, reset the interface and return
+ if (!aTable || ![aTable length]) {
+ [[self onMainThread] setTableDetails:nil];
return;
}
- // Enable edit table button
- [editTableButton setEnabled:enableInteraction];
-
- //query started
+ // Send the query started/working notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
- //perform queries and load results in array (each row as a dictionary)
- tableSourceResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]] retain];
+ // Retrieve the column information for this table.
+ // TODO: update this and indexes to use TableData at some point - tiny bit more parsing required...
+ tableSourceResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [aTable backtickQuotedString]]] retain];
+
+ // If an error occurred, reset the interface and abort
if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
NSString *errorMessage = [NSString stringWithString:[mySQLConnection getLastErrorMessage]];
- [tableFields removeAllObjects];
- [indexes removeAllObjects];
- [tableSourceView reloadData];
- [tablesListInstance updateTables:self];
- [indexView reloadData];
- [addFieldButton setEnabled:NO];
- [copyFieldButton setEnabled:NO];
- [removeFieldButton setEnabled:NO];
- [addIndexButton setEnabled:NO];
- [removeIndexButton setEnabled:NO];
- [editTableButton setEnabled:NO];
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[self onMainThread] setTableDetails:nil];
+
SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"),
nil, nil, [NSApp mainWindow], self, nil, nil, nil,
[NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"),
@@ -122,29 +88,20 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
return;
}
- [tableSourceResult setReturnDataAsStrings:YES];
-
- // listFieldsFromTable is broken in the current version of the framework (no back-ticks for table name)!
- // tableSourceResult = [[mySQLConnection listFieldsFromTable:selectedTable] retain];
- // [tableFields setArray:[[self fetchResultAsArray:tableSourceResult] retain]];
- [tableFields setArray:[self fetchResultAsArray:tableSourceResult]];
+ // Process the field names into a local array of dictionaries
+ theTableFields = [self fetchResultAsArray:tableSourceResult];
[tableSourceResult release];
- indexResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW INDEX FROM %@", [selectedTable backtickQuotedString]]] retain];
+ // Retrieve the indexes for the table
+ indexResult = [[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW INDEX FROM %@", [aTable backtickQuotedString]]] retain];
+
+ // If an error occurred, reset the interface and abort
if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
NSString *errorMessage = [NSString stringWithString:[mySQLConnection getLastErrorMessage]];
- [tableFields removeAllObjects];
- [indexes removeAllObjects];
- [tableSourceView reloadData];
- [tablesListInstance updateTables:self];
- [indexView reloadData];
- [addFieldButton setEnabled:NO];
- [copyFieldButton setEnabled:NO];
- [removeFieldButton setEnabled:NO];
- [addIndexButton setEnabled:NO];
- [removeIndexButton setEnabled:NO];
- [editTableButton setEnabled:NO];
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[self onMainThread] setTableDetails:nil];
+
SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"),
nil, nil, [NSApp mainWindow], self, nil, nil, nil,
[NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"),
@@ -152,33 +109,19 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
if (indexResult) [indexResult release];
return;
}
- [indexResult setReturnDataAsStrings:YES];
- // [indexes setArray:[[self fetchResultAsArray:indexResult] retain]];
- [indexes setArray:[self fetchResultAsArray:indexResult]];
+
+ // Process the indexes into a local array of dictionaries
+ theTableIndexes = [self fetchResultAsArray:indexResult];
[indexResult release];
-
- //get table default values
- if ( defaultValues ) {
- [defaultValues release];
- defaultValues = nil;
- }
-
- tempDefaultValues = [NSMutableDictionary dictionary];
- for ( i = 0 ; i < [tableFields count] ; i++ ) {
- [tempDefaultValues setObject:[[tableFields objectAtIndex:i] objectForKey:@"Default"] forKey:[[tableFields objectAtIndex:i] objectForKey:@"Field"]];
- }
- defaultValues = [[NSDictionary dictionaryWithDictionary:tempDefaultValues] retain];
-
- //put field length and extras in separate key
- enumerator = [tableFields objectEnumerator];
- while ( (field = [enumerator nextObject]) ) {
+ // Process all the fields to normalise keys and add additional information
+ for (id theField in theTableFields) {
NSString *type;
NSString *length;
NSString *extras;
// Set up the field parser with the type definition
- fieldParser = [[SPSQLParser alloc] initWithString:[field objectForKey:@"Type"]];
+ fieldParser = [[SPSQLParser alloc] initWithString:[theField objectForKey:@"Type"]];
// Pull out the field type; if no brackets are found, this returns nil - in which case simple values can be used.
type = [fieldParser trimAndReturnStringToCharacter:'(' trimmingInclusively:YES returningInclusively:NO];
@@ -207,7 +150,7 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
[valueParser setString:[possibleValues objectAtIndex:i]];
[possibleValues replaceObjectAtIndex:i withObject:[valueParser unquotedString]];
}
- [enumFields setObject:[NSArray arrayWithArray:possibleValues] forKey:[field objectForKey:@"Field"]];
+ [theTableEnumLists setObject:[NSArray arrayWithArray:possibleValues] forKey:[theField objectForKey:@"Field"]];
[possibleValues release];
[valueParser release];
}
@@ -215,72 +158,40 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
// For timestamps check to see whether "on update CURRENT_TIMESTAMP" - not returned
// by SHOW COLUMNS - should be set from the table data store
if ([type isEqualToString:@"timestamp"]
- && [[[tableDataInstance columnWithName:[field objectForKey:@"Field"]] objectForKey:@"onupdatetimestamp"] integerValue])
+ && [[[tableDataInstance columnWithName:[theField objectForKey:@"Field"]] objectForKey:@"onupdatetimestamp"] integerValue])
{
- [field setObject:@"on update CURRENT_TIMESTAMP" forKey:@"Extra"];
+ [theField setObject:@"on update CURRENT_TIMESTAMP" forKey:@"Extra"];
}
- // scan extras for values like unsigned, zerofill, binary
+ // Scan extras for values like unsigned, zerofill, binary
extrasArray = [extras componentsSeparatedByString:@" "];
- extrasEnumerator = [extrasArray objectEnumerator];
-
- while ( (extra = [extrasEnumerator nextObject]) ) {
- if ( [extra isEqualToString:@"unsigned"] ) {
- [field setObject:@"1" forKey:@"unsigned"];
- } else if ( [extra isEqualToString:@"zerofill"] ) {
- [field setObject:@"1" forKey:@"zerofill"];
- } else if ( [extra isEqualToString:@"binary"] ) {
- [field setObject:@"1" forKey:@"binary"];
+ for (id extra in extrasArray) {
+ if ([extra isEqualToString:@"unsigned"]) {
+ [theField setObject:@"1" forKey:@"unsigned"];
+ } else if ([extra isEqualToString:@"zerofill"]) {
+ [theField setObject:@"1" forKey:@"zerofill"];
+ } else if ([extra isEqualToString:@"binary"]) {
+ [theField setObject:@"1" forKey:@"binary"];
} else {
- if ( ![extra isEqualToString:@""] )
+ if (![extra isEqualToString:@""])
NSLog(@"ERROR: unknown option in field definition: %@", extra);
}
}
-
- [field setObject:type forKey:@"Type"];
- [field setObject:length forKey:@"Length"];
- }
-
- // If a view is selected, disable the buttons; otherwise enable.
- BOOL editingEnabled = ([tablesListInstance tableType] == SP_TABLETYPE_TABLE) && enableInteraction;
- [addFieldButton setEnabled:editingEnabled];
- [addIndexButton setEnabled:editingEnabled];
-
- //the following three buttons will only be enabled if a row field/index is selected!
- [copyFieldButton setEnabled:NO];
- [removeFieldButton setEnabled:NO];
- [removeIndexButton setEnabled:NO];
-
- //add columns to indexedColumnsField
- [indexedColumnsField removeAllItems];
- enumerator = [tableFields objectEnumerator];
-
- while ( (field = [enumerator nextObject]) ) {
- [indexedColumnsField addItemWithObjectValue:[field objectForKey:@"Field"]];
- }
-
- if ( [tableFields count] < 10 ) {
- [indexedColumnsField setNumberOfVisibleItems:[tableFields count]];
- } else {
- [indexedColumnsField setNumberOfVisibleItems:10];
+
+ [theField setObject:type forKey:@"Type"];
+ [theField setObject:length forKey:@"Length"];
}
-
- [indexView reloadData];
- [tableSourceView reloadData];
-
- // display and *then* tile to force scroll bars to be in the correct position
- [[tableSourceView enclosingScrollView] display];
- [[tableSourceView enclosingScrollView] tile];
-
- // Enable 'Duplicate field' if at least one field is specified
- // if no field is selected 'Duplicate field' will copy the last field
- // Enable 'Duplicate field' only for tables!
- if ([tablesListInstance tableType] == SP_TABLETYPE_TABLE)
- [copyFieldButton setEnabled:enableInteraction && ([tableSourceView numberOfRows] > 0)];
- else
- [copyFieldButton setEnabled:NO];
- //query finished
+ // Set up the table details for the new table, and request an data/interface update
+ NSDictionary *tableDetails = [NSDictionary dictionaryWithObjectsAndKeys:
+ aTable, @"name",
+ theTableFields, @"tableFields",
+ theTableIndexes, @"tableIndexes",
+ theTableEnumLists, @"enumLists",
+ nil];
+ [[self onMainThread] setTableDetails:tableDetails];
+
+ // Send the query finished/work complete notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
}
@@ -294,6 +205,75 @@ loads aTable, put it in an array, update the tableViewColumns and reload the tab
[self loadTable:selectedTable];
}
+/**
+ * Update stored table details and update the interface to match the supplied
+ * table details.
+ * Should be called on the main thread.
+ */
+- (void) setTableDetails:(NSDictionary *)tableDetails
+{
+ NSString *newTableName = [tableDetails objectForKey:@"name"];
+ NSMutableDictionary *newDefaultValues;
+ BOOL enableInteraction = ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableStructure] || ![tableDocumentInstance isWorking];
+
+ // Update the selected table name
+ if (selectedTable) [selectedTable release], selectedTable = nil;
+ if (newTableName) selectedTable = [[NSString alloc] initWithString:newTableName];
+
+ // Reset the table store and display
+ [enumFields removeAllObjects];
+ [tableSourceView deselectAll:self];
+ [indexView deselectAll:self];
+ [tableFields removeAllObjects];
+ [indexes removeAllObjects];
+ [addFieldButton setEnabled:NO];
+ [copyFieldButton setEnabled:NO];
+ [removeFieldButton setEnabled:NO];
+ [addIndexButton setEnabled:NO];
+ [removeIndexButton setEnabled:NO];
+ [editTableButton setEnabled:NO];
+
+ // If no table is selected, refresh the table display to blank and return
+ if (!selectedTable) {
+ [tableSourceView reloadData];
+ [indexView reloadData];
+ return;
+ }
+
+ // Update the fields and indexes stores
+ [tableFields setArray:[tableDetails objectForKey:@"tableFields"]];
+ [indexes setArray:[tableDetails objectForKey:@"tableIndexes"]];
+
+ // Update the default values array and the indexed column fields control
+ [indexedColumnsField removeAllItems];
+ if (defaultValues) [defaultValues release], defaultValues = nil;
+ newDefaultValues = [NSMutableDictionary dictionaryWithCapacity:[tableFields count]];
+ for (id theField in tableFields) {
+ [newDefaultValues setObject:[theField objectForKey:@"Default"] forKey:[theField objectForKey:@"Field"]];
+ [indexedColumnsField addItemWithObjectValue:[theField objectForKey:@"Field"]];
+ }
+ defaultValues = [[NSDictionary dictionaryWithDictionary:newDefaultValues] retain];
+
+ // Only show up to ten items in the indexed column fields control
+ if ([tableFields count] < 10) {
+ [indexedColumnsField setNumberOfVisibleItems:[tableFields count]];
+ } else {
+ [indexedColumnsField setNumberOfVisibleItems:10];
+ }
+
+ // Enable the edit table button
+ [editTableButton setEnabled:enableInteraction];
+
+ // If a view is selected, disable the buttons; otherwise enable.
+ BOOL editingEnabled = ([tablesListInstance tableType] == SP_TABLETYPE_TABLE) && enableInteraction;
+ [addFieldButton setEnabled:editingEnabled];
+ [addIndexButton setEnabled:editingEnabled];
+
+ // Reload the views
+ [indexView reloadData];
+ [tableSourceView reloadData];
+}
+
#pragma mark -
#pragma mark Edit methods
@@ -615,36 +595,37 @@ closes the keySheet
}
}
-/*
-fetches the result as an array with a dictionary for each row in it
-*/
+/**
+ * Converts the supplied result to an array containing a (mutable) dictionary for each row
+ */
- (NSArray *)fetchResultAsArray:(MCPResult *)theResult
{
NSUInteger numOfRows = [theResult numOfRows];
NSMutableArray *tempResult = [NSMutableArray arrayWithCapacity:numOfRows];
NSMutableDictionary *tempRow;
NSArray *keys;
- id key;
NSInteger i;
- Class nullClass = [NSNull class];
id prefsNullValue = [prefs objectForKey:SPNullValue];
+ // Ensure table information is returned as strings to avoid problems with some server versions
+ [theResult setReturnDataAsStrings:YES];
+
if (numOfRows) [theResult dataSeek:0];
for ( i = 0 ; i < numOfRows ; i++ ) {
tempRow = [NSMutableDictionary dictionaryWithDictionary:[theResult fetchRowAsDictionary]];
- //use NULL string from preferences instead of the NSNull oject returned by the framework
+ // Replace NSNull instances with the NULL string from preferences
keys = [tempRow allKeys];
- for (NSInteger j = 0; j < [keys count] ; j++) {
- key = NSArrayObjectAtIndex(keys, j);
- if ( [[tempRow objectForKey:key] isMemberOfClass:nullClass] )
- [tempRow setObject:prefsNullValue forKey:key];
+ for (id theKey in keys) {
+ if ([[tempRow objectForKey:theKey] isNSNull])
+ [tempRow setObject:prefsNullValue forKey:theKey];
}
- // change some fields to be more human-readable or GUI compatible
- if ( [[tempRow objectForKey:@"Extra"] isEqualToString:@""] ) {
+
+ // Update some fields to be more human-readable or GUI compatible
+ if ([[tempRow objectForKey:@"Extra"] isEqualToString:@""]) {
[tempRow setObject:@"None" forKey:@"Extra"];
}
- if ( [[tempRow objectForKey:@"Null"] isEqualToString:@"YES"] ) {
+ if ([[tempRow objectForKey:@"Null"] isEqualToString:@"YES"]) {
[tempRow setObject:@"1" forKey:@"Null"];
} else {
[tempRow setObject:@"0" forKey:@"Null"];
@@ -1690,7 +1671,7 @@ would result in a position change.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Check whether a save of the current fields row is required.
- if (![self saveRowOnDeselect]) return;
+ if (![[self onMainThread] saveRowOnDeselect]) return;
if (![[indexedColumnsField stringValue] isEqualToString:@""]) {