aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPTableRelations.m
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2011-09-04 13:30:26 +0000
committerstuconnolly <stuart02@gmail.com>2011-09-04 13:30:26 +0000
commit7b69d7481a828f4466335dd988da5ee3bc35af33 (patch)
treef10244d4300b7ef272062cdcf63535730e4f8131 /Source/SPTableRelations.m
parent41d0618a58f4f047ce453577d1b2b01a29761705 (diff)
downloadsequelpro-7b69d7481a828f4466335dd988da5ee3bc35af33.tar.gz
sequelpro-7b69d7481a828f4466335dd988da5ee3bc35af33.tar.bz2
sequelpro-7b69d7481a828f4466335dd988da5ee3bc35af33.zip
Fix for issue 1168. Whenever the database is changed load all of the currently used relation names in a backgroud thread. In the event that the user opens the add new relation sheet during this process, the ability to enter a relation name and confirm the addition is disabled until the retrieval process is complete. There is also a new progress indicator on the add sheet to indicate this.
Diffstat (limited to 'Source/SPTableRelations.m')
-rw-r--r--Source/SPTableRelations.m199
1 files changed, 130 insertions, 69 deletions
diff --git a/Source/SPTableRelations.m b/Source/SPTableRelations.m
index 01fc4a6e..a26e9039 100644
--- a/Source/SPTableRelations.m
+++ b/Source/SPTableRelations.m
@@ -30,10 +30,14 @@
#import "SPTableView.h"
#import "SPAlertSheets.h"
+static NSString *SPRemoveRelation = @"SPRemoveRelation";
+
@interface SPTableRelations ()
- (void)_refreshRelationDataForcingCacheRefresh:(BOOL)clearAllCaches;
- (void)_updateAvailableTableColumns;
+- (void)_loadUsedRelationNamesInBackground;
+- (void)_relationNamesLoaded;
@end
@@ -51,6 +55,8 @@
relationData = [[NSMutableArray alloc] init];
prefs = [NSUserDefaults standardUserDefaults];
takenConstraintNames = [[NSMutableArray alloc] init];
+
+ isRetrievingRelationNames = NO;
}
return self;
@@ -176,7 +182,6 @@
*/
- (IBAction)addRelation:(id)sender
{
-
// Check whether table editing is permitted (necessary as some actions - eg table double-click - bypass validation)
if ([tableDocumentInstance isWorking] || [tablesListInstance tableType] != SPTableTypeTable) return;
@@ -208,17 +213,6 @@
[refTablePopUpButton addItemWithTitle:[[result fetchRowAsArray] objectAtIndex:0]];
}
- // Get a list of used constraint names for this db
- [takenConstraintNames removeAllObjects];
- result = [connection queryString:[NSString stringWithFormat:@"SELECT DISTINCT constraint_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY' AND constraint_schema = %@", [[tableDocumentInstance database] tickQuotedString]]];
-
- [result dataSeek:0];
-
- for (NSUInteger i = 0; i < [result numOfRows]; i++)
- {
- [takenConstraintNames addObject:[[[result fetchRowAsArray] objectAtIndex:0] lowercaseString]];
- }
-
// Reset other fields
[constraintName setStringValue:@""];
[onDeletePopUpButton selectItemAtIndex:0];
@@ -258,7 +252,7 @@
[[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask];
[[buttons objectAtIndex:1] setKeyEquivalent:@"\r"];
- [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"removeRelation"];
+ [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:SPRemoveRelation];
}
}
@@ -293,7 +287,8 @@
[addRelationButton setEnabled:enableInteraction];
[refreshRelationsButton setEnabled:enableInteraction];
[relationsTableView setEnabled:YES];
- } else {
+ }
+ else {
[addRelationButton setEnabled:NO];
[refreshRelationsButton setEnabled:NO];
[relationsTableView setEnabled:NO];
@@ -307,34 +302,31 @@
#pragma mark -
#pragma mark TextField delegate methods
-- (void)controlTextDidChange:(NSNotification *)aNotification
-{
- id field = [aNotification object];
-
+- (void)controlTextDidChange:(NSNotification *)notification
+{
// Make sure the user does not enter a taken name
- if (field == constraintName) {
+ if ([notification object] == constraintName) {
- NSString *userValue = [[constraintName stringValue] lowercaseString];
BOOL taken = NO;
- for(NSString *takenName in takenConstraintNames) {
- if([takenName isEqualToString:userValue]) {
+ NSString *userValue = [[constraintName stringValue] lowercaseString];
+
+ for (NSString *takenName in takenConstraintNames)
+ {
+ if ([takenName isEqualToString:userValue]) {
taken = YES;
break;
}
}
- //make field red and disable add button
- if(taken) {
+ // Make field red and disable add button
+ if (taken) {
[constraintName setTextColor:[NSColor redColor]];
[confirmAddRelationButton setEnabled:NO];
}
- //reset
else {
[constraintName setTextColor:[NSColor controlTextColor]];
[confirmAddRelationButton setEnabled:YES];
}
-
- return;
}
}
@@ -366,7 +358,7 @@
* Double-click action on table cells - for the time being, return
* NO to disable editing.
*/
-- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
if ([tableDocumentInstance isWorking]) return NO;
@@ -376,7 +368,7 @@
/**
* Disable row selection while the document is working.
*/
-- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
+- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex
{
return ![tableDocumentInstance isWorking];
}
@@ -419,6 +411,26 @@
#pragma mark Other
/**
+ * Loads the currently used relation names from information_schema.table_constraints.
+ */
+- (void)loadUsedRelationNames
+{
+ if (isRetrievingRelationNames) return;
+
+ [dataProgressIndicator setHidden:NO];
+ [dataProgressIndicator startAnimation:self];
+
+ [progressStatusTextField setHidden:NO];
+
+ [constraintName setEnabled:NO];
+ [confirmAddRelationButton setEnabled:NO];
+
+ isRetrievingRelationNames = YES;
+
+ [NSThread detachNewThreadSelector:@selector(_loadUsedRelationNamesInBackground) toTarget:self withObject:nil];
+}
+
+/**
* Returns an array of relation data to be used for printing purposes. The first element in the array is always
* an array of the columns and each subsequent element is an array of relation data.
*/
@@ -462,7 +474,7 @@
*/
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
{
- if ([contextInfo isEqualToString:@"removeRelation"]) {
+ if ([contextInfo isEqualToString:SPRemoveRelation]) {
if (returnCode == NSAlertDefaultReturn) {
@@ -536,22 +548,7 @@
}
#pragma mark -
-
-/*
- * Dealloc.
- */
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:SPUseMonospacedFonts];
-
- [relationData release], relationData = nil;
-
- [takenConstraintNames release];
- takenConstraintNames = nil;
-
- [super dealloc];
-}
+#pragma mark Private API
/**
* Refresh the displayed relations, optionally forcing a refresh of the underlying cache.
@@ -561,22 +558,22 @@
[relationData removeAllObjects];
if ([tablesListInstance tableType] == SPTableTypeTable) {
-
+
if (clearAllCaches) [tableDataInstance updateInformationForCurrentTable];
-
+
NSArray *constraints = [tableDataInstance getConstraints];
-
+
for (NSDictionary *constraint in constraints)
{
[relationData addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [constraint objectForKey:@"name"], @"name",
- [[constraint objectForKey:@"columns"] objectAtIndex:0], @"columns",
- [constraint objectForKey:@"ref_table"], @"fk_table",
- [constraint objectForKey:@"ref_columns"], @"fk_columns",
- ([constraint objectForKey:@"update"] ? [constraint objectForKey:@"update"] : @""), @"on_update",
- ([constraint objectForKey:@"delete"] ? [constraint objectForKey:@"delete"] : @""), @"on_delete",
- nil]];
-
+ [constraint objectForKey:@"name"], @"name",
+ [[constraint objectForKey:@"columns"] objectAtIndex:0], @"columns",
+ [constraint objectForKey:@"ref_table"], @"fk_table",
+ [constraint objectForKey:@"ref_columns"], @"fk_columns",
+ ([constraint objectForKey:@"update"] ? [constraint objectForKey:@"update"] : @""), @"on_update",
+ ([constraint objectForKey:@"delete"] ? [constraint objectForKey:@"delete"] : @""), @"on_delete",
+ nil]];
+
}
}
@@ -591,24 +588,24 @@
{
NSString *column = [columnPopUpButton titleOfSelectedItem];
NSString *table = [refTablePopUpButton titleOfSelectedItem];
-
+
[tableDataInstance resetAllData];
[tableDataInstance updateInformationForCurrentTable];
-
+
NSDictionary *columnInfo = [[tableDataInstance columnWithName:column] copy];
-
+
[refColumnPopUpButton setEnabled:NO];
[confirmAddRelationButton setEnabled:NO];
-
+
[refColumnPopUpButton removeAllItems];
-
+
[tableDataInstance resetAllData];
NSDictionary *tableInfo = [tableDataInstance informationForTable:table];
-
+
NSArray *columns = [tableInfo objectForKey:@"columns"];
-
+
NSMutableArray *validColumns = [NSMutableArray array];
-
+
// Only add columns of the same data type
for (NSDictionary *aColumn in columns)
{
@@ -616,17 +613,81 @@
[validColumns addObject:[aColumn objectForKey:@"name"]];
}
}
-
+
// Add the valid columns
if ([validColumns count] > 0) {
NSArray *columnTitles = ([prefs boolForKey:SPAlphabeticalTableSorting])? [validColumns sortedArrayUsingSelector:@selector(compare:)] : validColumns;
+
[refColumnPopUpButton addItemsWithTitles:columnTitles];
-
+
[refColumnPopUpButton setEnabled:YES];
- [confirmAddRelationButton setEnabled:YES];
+
+ if (!isRetrievingRelationNames) {
+ [confirmAddRelationButton setEnabled:YES];
+ }
}
-
+
[columnInfo release];
}
+/**
+ * Loads all of the current foreign key relationship names for the current database. This method should be
+ * spawned on a separate thread.
+ */
+- (void)_loadUsedRelationNamesInBackground
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [takenConstraintNames removeAllObjects];
+
+ MCPResult *result = [connection queryString:[NSString stringWithFormat:@"SELECT DISTINCT constraint_name FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY' AND constraint_schema = %@", [[tableDocumentInstance database] tickQuotedString]]];
+
+ [result dataSeek:0];
+
+ for (NSUInteger i = 0; i < [result numOfRows]; i++)
+ {
+ [takenConstraintNames addObject:[[[result fetchRowAsArray] objectAtIndex:0] lowercaseString]];
+ }
+
+ // Update the UI on the main thread
+ [self performSelectorOnMainThread:@selector(_relationNamesLoaded) withObject:nil waitUntilDone:NO];
+
+ [pool release];
+}
+
+/**
+ * Called on the main thread, once all the current table relation names have been loaded and re-enables all
+ * the relevant UI controls.
+ */
+- (void)_relationNamesLoaded
+{
+ [dataProgressIndicator setHidden:YES];
+ [dataProgressIndicator stopAnimation:self];
+
+ [progressStatusTextField setHidden:YES];
+
+ [constraintName setEnabled:YES];
+ [confirmAddRelationButton setEnabled:YES];
+
+ [addRelationPanel makeFirstResponder:constraintName];
+
+ isRetrievingRelationNames = NO;
+}
+
+#pragma mark -
+
+/*
+ * Dealloc.
+ */
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:SPUseMonospacedFonts];
+
+ [relationData release], relationData = nil;
+ [takenConstraintNames release], takenConstraintNames = nil;
+
+ [super dealloc];
+}
+
@end