aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h9
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m668
-rw-r--r--Source/CMTextView.m53
-rw-r--r--Source/SPNarrowDownCompletion.h14
-rw-r--r--Source/SPNarrowDownCompletion.m118
-rw-r--r--Source/SPNavigatorController.h4
-rw-r--r--Source/SPNavigatorController.m64
-rw-r--r--Source/TableDocument.m5
8 files changed, 558 insertions, 377 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
index 05078a60..1945b3de 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
@@ -173,7 +173,8 @@
BOOL delegateQueryLogging;
BOOL delegateResponseToWillQueryString;
BOOL delegateSupportsConnectionLostDecisions;
- BOOL isQueryingDbStructure;
+ NSInteger isQueryingDbStructure;
+ BOOL lockQuerying;
// Pointers
IMP cStringPtr;
@@ -286,6 +287,12 @@ void performThreadedKeepAlive(void *ptr);
- (void)queryDbStructureWithUserInfo:(NSDictionary*)userInfo;
- (NSDictionary *)getDbStructure;
- (NSArray *)getAllKeysOfDbStructure;
+- (BOOL)isQueryingDatabaseStructure;
+- (void)incrementQueryingDbStructure;
+- (void)decrementQueryingDbStructure;
+- (void)lockQuerying;
+- (void)unlockQuerying;
+- (void)updateGlobalVariablesWith:(NSDictionary*)object;
// Server information
- (NSString *)clientInfo;
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
index fd9842aa..0bf5d713 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
@@ -118,7 +118,8 @@ static BOOL sTruncateLongFieldInLogs = YES;
structure = [[NSMutableDictionary alloc] initWithCapacity:1];
allKeysofDbStructure = [[NSMutableArray alloc] initWithCapacity:20];
- isQueryingDbStructure = NO;
+ isQueryingDbStructure = 0;
+ lockQuerying = NO;
connectionThreadId = 0;
maxAllowedPacketSize = -1;
@@ -1861,365 +1862,416 @@ void performThreadedKeepAlive(void *ptr)
{
NSAutoreleasePool *queryPool = [[NSAutoreleasePool alloc] init];
- if (!isQueryingDbStructure) {
+ // Requests are queued
+ while(isQueryingDbStructure > 0) { usleep(1000000); }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureIsUpdating" object:delegate];
+
+ NSString *SPUniqueSchemaDelimiter = @"￸";
+
+ NSString *connectionID;
+ if([delegate respondsToSelector:@selector(connectionID)])
+ connectionID = [NSString stringWithString:[[self delegate] connectionID]];
+ else
+ connectionID = @"_";
+
+ // Re-init with already cached data from navigator controller
+ NSMutableDictionary *queriedStructure = [NSMutableDictionary dictionary];
+ NSDictionary *dbstructure = [[[self delegate] getDbStructure] retain];
+ [queriedStructure setDictionary:[NSMutableDictionary dictionaryWithDictionary:dbstructure]];
+
+ NSMutableArray *queriedStructureKeys = [NSMutableArray array];
+ NSArray *dbStructureKeys = [[[self delegate] allSchemaKeys] retain];
+ [queriedStructureKeys setArray:dbStructureKeys];
+ if(dbstructure) [dbstructure release], dbstructure = nil;
+ if(dbStructureKeys) [dbStructureKeys release], dbStructureKeys = nil;
+
+ BOOL removeAddFlag = NO;
+
+ // Add all known databases coming from connection if they aren't parsed yet
+ NSArray *dbs = [[NSString stringWithFormat:@"%@%@%@",
+ [[[self delegate] allSystemDatabaseNames] componentsJoinedByString:SPUniqueSchemaDelimiter],
+ SPUniqueSchemaDelimiter,
+ [[[self delegate] allDatabaseNames] componentsJoinedByString:SPUniqueSchemaDelimiter]]
+ componentsSeparatedByString:SPUniqueSchemaDelimiter];
+
+ for(id db in dbs) {
+ NSString *dbid = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db];
+ if(![queriedStructure objectForKey:dbid]) {
+ removeAddFlag = YES;
+ [queriedStructure setObject:db forKey:dbid];
+ [queriedStructureKeys addObject:dbid];
+ }
+ }
-// NSLog(@"queryDbStructureWithUserInfo called with %@ and db %@", [userInfo description], [[[self delegate] database] description]);
+ // Remove deleted databases in structure and keys in allKeysofDbStructure
+ // Use a dict to avoid <NSCFDictionary> was mutated while being enumerated. while iterating via allKeys
+ NSArray *keys = [queriedStructure allKeys];
+ for(id key in keys) {
+ NSString *db = [[key componentsSeparatedByString:SPUniqueSchemaDelimiter] objectAtIndex:1];
+ if(![dbs containsObject:db]) {
+ removeAddFlag = YES;
+ [queriedStructure removeObjectForKey:key];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", key, SPUniqueSchemaDelimiter]];
+ [queriedStructureKeys filterUsingPredicate:predicate];
+ [queriedStructureKeys removeObject:key];
+ }
+ }
- isQueryingDbStructure = YES;
+ NSString *currentDatabase = nil;
+ if([delegate respondsToSelector:@selector(database)])
+ currentDatabase = [[self delegate] database];
+
+ if(!currentDatabase || (currentDatabase && ![currentDatabase length])) {
+
+ // Updating the global variables and make sure that no request reads these global variables
+ // while updating
+ [self performSelectorOnMainThread:@selector(lockQuerying) withObject:nil waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(updateGlobalVariablesWith:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[queriedStructure retain], @"structure", [queriedStructureKeys retain], @"keys", nil] waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
+ if(removeAddFlag)
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+ [queryPool release];
+ return;
+ }
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureIsUpdating" object:delegate];
+ NSString *db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, currentDatabase];
- NSString *SPUniqueSchemaDelimiter = @"￸";
+ // mysql and information_schema's schema will never change thus query data only once
+ if([currentDatabase isEqualToString:@"mysql"]) {
+ if([queriedStructure objectForKey:db_id] && [[queriedStructure objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
- NSString *connectionID;
- if([delegate respondsToSelector:@selector(connectionID)])
- connectionID = [NSString stringWithString:[[self delegate] connectionID]];
- else
- connectionID = @"_";
-
- // Re-init with already cached data from navigator controller
- NSDictionary *dbstructure = [[[self delegate] getDbStructure] retain];
- [structure removeAllObjects];
- [structure setObject:[NSMutableDictionary dictionaryWithDictionary:dbstructure] forKey:connectionID];
- NSArray *allStructureKeys = [[self delegate] allSchemaKeys];
- if(allStructureKeys && [allStructureKeys count]) {
- if(allKeysofDbStructure) [allKeysofDbStructure release];
- allKeysofDbStructure = nil;
- allKeysofDbStructure = [[NSMutableArray alloc] initWithCapacity:[allKeysofDbStructure count]];
- [allKeysofDbStructure setArray:allStructureKeys];
- }
- if(dbstructure) [dbstructure release], dbstructure = nil;
-
- BOOL removeAddFlag = NO;
-
- // Add all known databases coming from connection if they aren't parsed yet
- NSArray *dbs = [[NSString stringWithFormat:@"%@%@%@",
- [[[self delegate] allSystemDatabaseNames] componentsJoinedByString:SPUniqueSchemaDelimiter],
- SPUniqueSchemaDelimiter,
- [[[self delegate] allDatabaseNames] componentsJoinedByString:SPUniqueSchemaDelimiter]]
- componentsSeparatedByString:SPUniqueSchemaDelimiter];
-
- for(id db in dbs) {
- NSString *dbid = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db];
- if(![[structure valueForKey:connectionID] objectForKey:dbid]) {
-// NSLog(@"added db %@", db);
- removeAddFlag = YES;
- [[structure valueForKey:connectionID] setObject:db forKey:dbid];
- [allKeysofDbStructure addObject:dbid];
- }
+ // Updating the global variables and make sure that no request reads these global variables
+ // while updating
+ [self performSelectorOnMainThread:@selector(lockQuerying) withObject:nil waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(updateGlobalVariablesWith:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[queriedStructure retain], @"structure", [queriedStructureKeys retain], @"keys", nil] waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
+ if(removeAddFlag)
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+ [queryPool release];
+ return;
}
-
- // Remove deleted databases in structure and keys in allKeysofDbStructure
- // Use a dict to avoid <NSCFDictionary> was mutated while being enumerated. while iterating via allKeys
- NSArray *keys = [[NSDictionary dictionaryWithDictionary:[structure valueForKey:connectionID]] allKeys];
- for(id key in keys) {
- NSString *db = [[key componentsSeparatedByString:SPUniqueSchemaDelimiter] objectAtIndex:1];
- if(![dbs containsObject:db]) {
-// NSLog(@"removed db %@", db);
- removeAddFlag = YES;
- if([[structure valueForKey:connectionID] isKindOfClass:[NSDictionary class]]) {
- [[structure valueForKey:connectionID] removeObjectForKey:key];
- }
- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", key, SPUniqueSchemaDelimiter]];
- [allKeysofDbStructure filterUsingPredicate:predicate];
- [allKeysofDbStructure removeObject:key];
- }
+ }
+ if([currentDatabase isEqualToString:@"information_schema"]) {
+ if([queriedStructure objectForKey:db_id] && [[queriedStructure objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
+ // Updating the global variables and make sure that no request reads these global variables
+ // while updating
+ [self performSelectorOnMainThread:@selector(lockQuerying) withObject:nil waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(updateGlobalVariablesWith:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[queriedStructure retain], @"structure", [queriedStructureKeys retain], @"keys", nil] waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
+ if(removeAddFlag)
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+ [queryPool release];
+ return;
}
+ }
- NSString *currentDatabase = nil;
- if([delegate respondsToSelector:@selector(database)])
- currentDatabase = [[self delegate] database];
-
- if(!currentDatabase || (currentDatabase && ![currentDatabase length])) {
-// NSLog(@"no db - return");
- isQueryingDbStructure = NO;
+ if(userInfo == nil || ![userInfo objectForKey:@"forceUpdate"]) {
+ if([queriedStructure objectForKey:db_id] && [[queriedStructure objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
+ // Updating the global variables and make sure that no request reads these global variables
+ // while updating
+ [self performSelectorOnMainThread:@selector(lockQuerying) withObject:nil waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(updateGlobalVariablesWith:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[queriedStructure retain], @"structure", [queriedStructureKeys retain], @"keys", nil] waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
if(removeAddFlag)
[[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
[queryPool release];
return;
}
+ }
- NSString *db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, currentDatabase];
+ NSArray *tables = [[[self delegate] valueForKeyPath:@"tablesListInstance"] allTableNames];
+ NSArray *tableviews = [[[self delegate] valueForKeyPath:@"tablesListInstance"] allViewNames];
- // mysql and information_schema's schema will never change thus query data only once
- if([currentDatabase isEqualToString:@"mysql"]) {
- if([[structure valueForKey:connectionID] objectForKey:db_id] && [[[structure valueForKey:connectionID] objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
-// NSLog(@"mysql was parsed - return");
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
- }
- if([currentDatabase isEqualToString:@"information_schema"]) {
- if([[structure valueForKey:connectionID] objectForKey:db_id] && [[[structure valueForKey:connectionID] objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
-// NSLog(@"information_schema was parsed - return");
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
- }
+ NSUInteger numberOfTables = 0;
+ if(tables && [tables count]) numberOfTables += [tables count];
+ if(tableviews && [tableviews count]) numberOfTables += [tableviews count];
- if(userInfo == nil || ![userInfo objectForKey:@"forceUpdate"]) {
- if([[structure valueForKey:connectionID] objectForKey:db_id] && [[[structure valueForKey:connectionID] objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
-// NSLog(@"no forceUpdate - return");
- isQueryingDbStructure = NO;
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
- }
+ // For future usage
+ NSString *affectedItem = nil;
+ NSInteger affectedItemType = -1;
+ if(userInfo && [userInfo objectForKey:@"affectedItem"]) {
+ affectedItem = [userInfo objectForKey:@"affectedItem"];
+ if([userInfo objectForKey:@"affectedItemType"])
+ affectedItemType = [[userInfo objectForKey:@"affectedItemType"] intValue];
+ else
+ affectedItem = nil;
+ }
- NSArray *tables = [[[self delegate] valueForKeyPath:@"tablesListInstance"] allTableNames];
- NSArray *tableviews = [[[self delegate] valueForKeyPath:@"tablesListInstance"] allViewNames];
-
- NSUInteger numberOfTables = 0;
- if(tables && [tables count]) numberOfTables += [tables count];
- if(tableviews && [tableviews count]) numberOfTables += [tableviews count];
-
- // For future usage
- NSString *affectedItem = nil;
- NSInteger affectedItemType = -1;
- if(userInfo && [userInfo objectForKey:@"affectedItem"]) {
- affectedItem = [userInfo objectForKey:@"affectedItem"];
- if([userInfo objectForKey:@"affectedItemType"])
- affectedItemType = [[userInfo objectForKey:@"affectedItemType"] intValue];
- else
- affectedItem = nil;
- }
+ // Delete all stored data for to be queried db
+ if([queriedStructure isKindOfClass:[NSDictionary class]])
+ [queriedStructure removeObjectForKey:db_id];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", db_id, SPUniqueSchemaDelimiter]];
+ [queriedStructureKeys filterUsingPredicate:predicate];
+ [queriedStructureKeys removeObject:db_id];
- // Delete all stored data for to be queried db
- if([[structure valueForKey:connectionID] isKindOfClass:[NSDictionary class]])
- [[structure valueForKey:connectionID] removeObjectForKey:db_id];
- NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", db_id, SPUniqueSchemaDelimiter]];
- [allKeysofDbStructure filterUsingPredicate:predicate];
- [allKeysofDbStructure removeObject:db_id];
- // Re-add currentDatabase in case that structure querying will fail
- [[structure valueForKey:connectionID] setObject:currentDatabase forKey:db_id];
+ // Re-add currentDatabase in case that structure querying will fail
+ [queriedStructure setObject:currentDatabase forKey:db_id];
- NSString *currentDatabaseEscaped = [currentDatabase stringByReplacingOccurrencesOfString:@"`" withString:@"``"];
+ NSString *currentDatabaseEscaped = [currentDatabase stringByReplacingOccurrencesOfString:@"`" withString:@"``"];
- MYSQL *structConnection = mysql_init(NULL);
- if (structConnection) {
- const char *theLogin = [self cStringFromString:connectionLogin];
- const char *theHost;
- const char *thePass = NULL;
- const char *theSocket;
- void *connectionSetupStatus;
+ MYSQL *structConnection = mysql_init(NULL);
+ if (structConnection) {
+ const char *theLogin = [self cStringFromString:connectionLogin];
+ const char *theHost;
+ const char *thePass = NULL;
+ const char *theSocket;
+ void *connectionSetupStatus;
- mysql_options(structConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+ mysql_options(structConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
- // Set up the host, socket and password as per the connect method
- if (!connectionHost || ![connectionHost length]) {
- theHost = NULL;
- } else {
- theHost = [self cStringFromString:connectionHost];
- }
- if (connectionSocket == nil || ![connectionSocket length]) {
- theSocket = kMCPConnectionDefaultSocket;
- } else {
- theSocket = [self cStringFromString:connectionSocket];
- }
- if (!connectionPassword) {
- if (delegate && [delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
- thePass = [self cStringFromString:[delegate keychainPasswordForConnection:self]];
- }
- } else {
- thePass = [self cStringFromString:connectionPassword];
+ // Set up the host, socket and password as per the connect method
+ if (!connectionHost || ![connectionHost length]) {
+ theHost = NULL;
+ } else {
+ theHost = [self cStringFromString:connectionHost];
+ }
+ if (connectionSocket == nil || ![connectionSocket length]) {
+ theSocket = kMCPConnectionDefaultSocket;
+ } else {
+ theSocket = [self cStringFromString:connectionSocket];
+ }
+ if (!connectionPassword) {
+ if (delegate && [delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
+ thePass = [self cStringFromString:[delegate keychainPasswordForConnection:self]];
}
+ } else {
+ thePass = [self cStringFromString:connectionPassword];
+ }
- // Connect
- connectionSetupStatus = mysql_real_connect(structConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
- thePass = NULL;
- if (connectionSetupStatus) {
- MYSQL_RES *theResult;
- MYSQL_ROW row;
+ // Connect
+ connectionSetupStatus = mysql_real_connect(structConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
+ thePass = NULL;
+ if (connectionSetupStatus) {
+ MYSQL_RES *theResult;
+ MYSQL_ROW row;
- NSStringEncoding theConnectionEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(structConnection)];
- NSString *charset;
+ NSStringEncoding theConnectionEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(structConnection)];
+ NSString *charset;
- if(numberOfTables > 2000) {
- NSLog(@"%ld items in database %@. Only 2000 items can be parsed. Stopped parsing.", numberOfTables, currentDatabase);
- isQueryingDbStructure = NO;
- [queryPool release];
- return;
- }
+ if(numberOfTables > 2000) {
+ NSLog(@"%ld items in database %@. Only 2000 items can be parsed. Stopped parsing.", numberOfTables, currentDatabase);
+ [queryPool release];
+ return;
+ }
+
+ [self performSelectorOnMainThread:@selector(incrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
-// NSLog(@"queryDbStructureWithUserInfo started");
+ NSUInteger uniqueCounter = 0; // used to make field data unique
- NSUInteger uniqueCounter = 0; // used to make field data unique
+ NSString *query = @"SET NAMES 'utf8'";
+ NSData *encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
+ const char *queryCString = [encodedQueryData bytes];
+ unsigned long queryCStringLength = [encodedQueryData length];
+ if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
+ ;
+ }
- NSString *query = @"SET NAMES 'utf8'";
+ [queriedStructureKeys addObject:db_id];
+
+ // Query all tables
+ for(NSString* table in tables) {
+ NSString *query = [NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`",
+ [table stringByReplacingOccurrencesOfString:@"`" withString:@"``"],
+ currentDatabaseEscaped];
NSData *encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
const char *queryCString = [encodedQueryData bytes];
unsigned long queryCStringLength = [encodedQueryData length];
if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
- ;
+ // NSLog(@"error %@", table);
+ continue;
+ }
+ theResult = mysql_use_result(structConnection);
+
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
+
+ [queriedStructureKeys addObject:table_id];
+
+ while(row = mysql_fetch_row(theResult)) {
+ NSString *field = [self stringWithUTF8CString:row[0]];
+ NSString *type = [self stringWithUTF8CString:row[1]];
+ NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
+ NSString *coll = [self stringWithUTF8CString:row[2]];
+ NSString *isnull = [self stringWithUTF8CString:row[3]];
+ NSString *key = [self stringWithUTF8CString:row[4]];
+ NSString *def = [self stringWithUTF8CString:row[5]];
+ NSString *extra = [self stringWithUTF8CString:row[6]];
+ NSString *priv = [self stringWithUTF8CString:row[7]];
+ NSString *comment = [self stringWithUTF8CString:row[8]];
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
+ NSArray *a = [coll componentsSeparatedByString:@"_"];
+ charset = ([a count]) ? [a objectAtIndex:0] : @"";
+
+ [queriedStructureKeys addObject:field_id];
+
+ if(![queriedStructure valueForKey:db_id] || [[queriedStructure valueForKey:db_id] isKindOfClass:[NSString class]] )
+ [queriedStructure setObject:[NSMutableDictionary dictionary] forKey:db_id];
+
+ if(![[queriedStructure valueForKey:db_id] valueForKey:table_id])
+ [[queriedStructure valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
+
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:[NSArray arrayWithObjects:type, def, isnull, charset, coll, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:@"0" forKey:@" struct_type "];
+ uniqueCounter++;
+ }
+ mysql_free_result(theResult);
+ usleep(10);
+ }
+ // Query all views
+ for(NSString* table in tableviews) {
+ NSString *query = [NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`",
+ [table stringByReplacingOccurrencesOfString:@"`" withString:@"``"],
+ currentDatabaseEscaped];
+ NSData *encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
+ const char *queryCString = [encodedQueryData bytes];
+ unsigned long queryCStringLength = [encodedQueryData length];
+ if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
+ // NSLog(@"error %@", table);
+ continue;
}
- [allKeysofDbStructure addObject:db_id];
-
- // Query all tables
- for(NSString* table in tables) {
- NSString *query = [NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`",
- [table stringByReplacingOccurrencesOfString:@"`" withString:@"``"],
- currentDatabaseEscaped];
- NSData *encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
- const char *queryCString = [encodedQueryData bytes];
- unsigned long queryCStringLength = [encodedQueryData length];
- if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
- // NSLog(@"error %@", table);
- continue;
- }
- theResult = mysql_use_result(structConnection);
-
- NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
+ theResult = mysql_use_result(structConnection);
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
- [allKeysofDbStructure addObject:table_id];
+ [queriedStructureKeys addObject:table_id];
- while(row = mysql_fetch_row(theResult)) {
- NSString *field = [self stringWithUTF8CString:row[0]];
- NSString *type = [self stringWithUTF8CString:row[1]];
- NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
- NSString *coll = [self stringWithUTF8CString:row[2]];
- NSString *isnull = [self stringWithUTF8CString:row[3]];
- NSString *key = [self stringWithUTF8CString:row[4]];
- NSString *def = [self stringWithUTF8CString:row[5]];
- NSString *extra = [self stringWithUTF8CString:row[6]];
- NSString *priv = [self stringWithUTF8CString:row[7]];
- NSString *comment = [self stringWithUTF8CString:row[8]];
- NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
- NSArray *a = [coll componentsSeparatedByString:@"_"];
- charset = ([a count]) ? [a objectAtIndex:0] : @"";
-
- [allKeysofDbStructure addObject:field_id];
-
- if(![[structure valueForKey:connectionID] valueForKey:db_id] || [[[structure valueForKey:connectionID] valueForKey:db_id] isKindOfClass:[NSString class]] )
- [[structure valueForKey:connectionID] setObject:[NSMutableDictionary dictionary] forKey:db_id];
-
- if(![[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id])
- [[[structure valueForKey:connectionID] valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
-
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:[NSArray arrayWithObjects:type, def, isnull, charset, coll, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:@"0" forKey:@" struct_type "];
- uniqueCounter++;
- }
- mysql_free_result(theResult);
- usleep(10);
+ NSString *charset;
+ while(row = mysql_fetch_row(theResult)) {
+ NSString *field = [self stringWithUTF8CString:row[0]];
+ NSString *type = [self stringWithUTF8CString:row[1]];
+ NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
+ NSString *coll = [self stringWithUTF8CString:row[2]];
+ NSString *isnull = [self stringWithUTF8CString:row[3]];
+ NSString *key = [self stringWithUTF8CString:row[4]];
+ NSString *def = [self stringWithUTF8CString:row[5]];
+ NSString *extra = [self stringWithUTF8CString:row[6]];
+ NSString *priv = [self stringWithUTF8CString:row[7]];
+ NSString *comment = [self stringWithUTF8CString:row[8]];
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
+ NSArray *a = [coll componentsSeparatedByString:@"_"];
+ charset = ([a count]) ? [a objectAtIndex:0] : @"";
+
+ [queriedStructureKeys addObject:field_id];
+
+ if(![queriedStructure valueForKey:db_id] || [[[queriedStructure valueForKey:connectionID] valueForKey:db_id] isKindOfClass:[NSString class]] )
+ [queriedStructure setObject:[NSMutableDictionary dictionary] forKey:db_id];
+
+ if(![[queriedStructure valueForKey:db_id] valueForKey:table_id])
+ [[queriedStructure valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
+
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:[NSArray arrayWithObjects:type, def, isnull, charset, coll, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:@"1" forKey:@" struct_type "];
+ uniqueCounter++;
}
- // Query all views
- for(NSString* table in tableviews) {
- NSString *query = [NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`",
- [table stringByReplacingOccurrencesOfString:@"`" withString:@"``"],
- currentDatabaseEscaped];
- NSData *encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
- const char *queryCString = [encodedQueryData bytes];
- unsigned long queryCStringLength = [encodedQueryData length];
- if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
- // NSLog(@"error %@", table);
- continue;
- }
+ mysql_free_result(theResult);
+ usleep(10);
+ }
+ if([self serverMajorVersion] >= 5) {
+ // Query for procedures and functions
+ query = [NSString stringWithFormat:@"SELECT * FROM `information_schema`.`ROUTINES` WHERE `information_schema`.`ROUTINES`.`ROUTINE_SCHEMA` = '%@'", [currentDatabase stringByReplacingOccurrencesOfString:@"'" withString:@"\'"]];
+ encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
+ queryCString = [encodedQueryData bytes];
+ queryCStringLength = [encodedQueryData length];
+ if (mysql_real_query(structConnection, queryCString, queryCStringLength) == 0) {
theResult = mysql_use_result(structConnection);
- NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
-
- [allKeysofDbStructure addObject:table_id];
-
- NSString *charset;
+ NSUInteger numberOfFields = mysql_num_fields(theResult);
while(row = mysql_fetch_row(theResult)) {
NSString *field = [self stringWithUTF8CString:row[0]];
- NSString *type = [self stringWithUTF8CString:row[1]];
- NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
- NSString *coll = [self stringWithUTF8CString:row[2]];
- NSString *isnull = [self stringWithUTF8CString:row[3]];
- NSString *key = [self stringWithUTF8CString:row[4]];
- NSString *def = [self stringWithUTF8CString:row[5]];
- NSString *extra = [self stringWithUTF8CString:row[6]];
- NSString *priv = [self stringWithUTF8CString:row[7]];
- NSString *comment = [self stringWithUTF8CString:row[8]];
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, field];
NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
- NSArray *a = [coll componentsSeparatedByString:@"_"];
- charset = ([a count]) ? [a objectAtIndex:0] : @"";
-
- [allKeysofDbStructure addObject:field_id];
-
- if(![[structure valueForKey:connectionID] valueForKey:db_id] || [[[structure valueForKey:connectionID] valueForKey:db_id] isKindOfClass:[NSString class]] )
- [[structure valueForKey:connectionID] setObject:[NSMutableDictionary dictionary] forKey:db_id];
-
- if(![[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id])
- [[[structure valueForKey:connectionID] valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
-
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:[NSArray arrayWithObjects:type, def, isnull, charset, coll, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:@"1" forKey:@" struct_type "];
+ NSString *type = ([[self stringWithUTF8CString:row[4]] isEqualToString:@"FUNCTION"]) ? @"3" : @"2";
+ NSString *dtd = [self stringWithUTF8CString:row[5]];
+ NSString *det = [self stringWithUTF8CString:row[11]];
+ NSString *access = [self stringWithUTF8CString:row[12]];
+ NSString *security_type = [self stringWithUTF8CString:row[14]];
+ NSString *definer = [self stringWithUTF8CString:row[19]];
+
+ [queriedStructureKeys addObject:table_id];
+ [queriedStructureKeys addObject:field_id];
+
+ if(![queriedStructure valueForKey:db_id] || [[queriedStructure valueForKey:db_id] isKindOfClass:[NSString class]] )
+ [queriedStructure setObject:[NSMutableDictionary dictionary] forKey:db_id];
+
+ if(![[queriedStructure valueForKey:db_id] valueForKey:table_id])
+ [[queriedStructure valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
+
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:
+ [NSArray arrayWithObjects:dtd, access, det, security_type, definer, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [[[queriedStructure valueForKey:db_id] valueForKey:table_id] setObject:type forKey:@" struct_type "];
uniqueCounter++;
}
mysql_free_result(theResult);
- usleep(10);
}
+ }
- if([self serverMajorVersion] >= 5) {
- // Query for procedures and functions
- query = [NSString stringWithFormat:@"SELECT * FROM `information_schema`.`ROUTINES` WHERE `information_schema`.`ROUTINES`.`ROUTINE_SCHEMA` = '%@'", [currentDatabase stringByReplacingOccurrencesOfString:@"'" withString:@"\'"]];
- encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
- queryCString = [encodedQueryData bytes];
- queryCStringLength = [encodedQueryData length];
- if (mysql_real_query(structConnection, queryCString, queryCStringLength) == 0) {
- theResult = mysql_use_result(structConnection);
- NSUInteger numberOfFields = mysql_num_fields(theResult);
- while(row = mysql_fetch_row(theResult)) {
- NSString *field = [self stringWithUTF8CString:row[0]];
- NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, field];
- NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
- NSString *type = ([[self stringWithUTF8CString:row[4]] isEqualToString:@"FUNCTION"]) ? @"3" : @"2";
- NSString *dtd = [self stringWithUTF8CString:row[5]];
- NSString *det = [self stringWithUTF8CString:row[11]];
- NSString *access = [self stringWithUTF8CString:row[12]];
- NSString *security_type = [self stringWithUTF8CString:row[14]];
- NSString *definer = [self stringWithUTF8CString:row[19]];
-
- [allKeysofDbStructure addObject:table_id];
- [allKeysofDbStructure addObject:field_id];
-
- if(![[structure valueForKey:connectionID] valueForKey:db_id] || [[[structure valueForKey:connectionID] valueForKey:db_id] isKindOfClass:[NSString class]] )
- [[structure valueForKey:connectionID] setObject:[NSMutableDictionary dictionary] forKey:db_id];
-
- if(![[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id])
- [[[structure valueForKey:connectionID] valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
-
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:
- [NSArray arrayWithObjects:dtd, access, det, security_type, definer, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
- [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:type forKey:@" struct_type "];
- uniqueCounter++;
- }
- mysql_free_result(theResult);
- }
- }
+ // Updating the global variables and make sure that no request reads these global variables
+ // while updating
+ [self performSelectorOnMainThread:@selector(lockQuerying) withObject:nil waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(updateGlobalVariablesWith:) withObject:[NSDictionary dictionaryWithObjectsAndKeys:[queriedStructure retain], @"structure", [queriedStructureKeys retain], @"keys", nil] waitUntilDone:YES];
+ [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
-// NSLog(@"queryDbStructureWithUserInfo done");
- // Notify that the structure querying has been performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+ mysql_close(structConnection);
- mysql_close(structConnection);
- }
+ // Notify that the structure querying has been performed
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
+
+ [self performSelectorOnMainThread:@selector(decrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
}
- } else {
- // Notify that the structure querying has been performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
}
-
- isQueryingDbStructure = NO;
+
[queryPool release];
}
+/*
+ * Update global variables on main thread to avoid accessing from different threads
+ */
+- (void)updateGlobalVariablesWith:(NSDictionary*)object
+{
+ NSString *connectionID = [[self delegate] connectionID];
+ if([connectionID length] < 2) return;
+ if(![structure valueForKey:connectionID])
+ [structure setObject:[NSMutableDictionary dictionary] forKey:connectionID];
+ [structure setObject:[[object objectForKey:@"structure"] retain] forKey:connectionID];
+ [allKeysofDbStructure setArray:[[object objectForKey:@"keys"] retain]];
+ usleep(100);
+}
+
+- (void)incrementQueryingDbStructure
+{
+ isQueryingDbStructure++;
+}
+
+- (void)decrementQueryingDbStructure
+{
+ isQueryingDbStructure--;
+ if(isQueryingDbStructure < 0) isQueryingDbStructure = 0;
+}
+
+- (BOOL)isQueryingDatabaseStructure
+{
+ return (isQueryingDbStructure > 0) ? YES : NO;
+}
+
+- (void)lockQuerying
+{
+ lockQuerying = YES;
+}
+
+- (void)unlockQuerying
+{
+ lockQuerying = NO;
+ usleep(50000);
+}
/**
* Returns a dict containing the structure of all available databases
*/
- (NSDictionary *)getDbStructure
{
- return [[structure copy] autorelease];
+ if(lockQuerying) return nil;
+ NSDictionary *d = [NSDictionary dictionaryWithDictionary:structure];
+ return d;
}
/**
@@ -2227,22 +2279,8 @@ void performThreadedKeepAlive(void *ptr)
*/
- (NSArray *)getAllKeysOfDbStructure
{
- NSArray *r = nil;
- @try
- {
- r = [NSArray arrayWithArray:allKeysofDbStructure];
- }
- @catch(id ae)
- {
- @try
- {
- r = [NSArray arrayWithArray:allKeysofDbStructure];
- }
- @catch(id ae)
- {
- return nil;
- }
- }
+ if(lockQuerying) return nil;
+ NSArray *r = [NSArray arrayWithArray:allKeysofDbStructure];
return r;
}
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 2b9879b6..91be7257 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -309,13 +309,12 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
else
connectionID = @"_";
- // NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
+ // Try to get structure data
NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[SPNavigatorController sharedNavigatorController] dbStructureForConnection:connectionID]];
if(dbs != nil && [dbs isKindOfClass:[NSDictionary class]] && [dbs count]) {
NSMutableArray *allDbs = [NSMutableArray array];
- @try { [allDbs addObjectsFromArray:[dbs allKeys]]; }
- @catch(id ae) { ; }
+ [allDbs addObjectsFromArray:[dbs allKeys]];
NSSortDescriptor *desc = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
NSMutableArray *sortedDbs = [NSMutableArray array];
@@ -520,11 +519,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void) doAutoCompletion
{
+ // Cancel autocompletion trigger
+ if([prefs boolForKey:SPCustomQueryAutoComplete])
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(doAutoCompletion)
+ object:nil];
+
if(completionIsOpen) return;
NSRange r = [self selectedRange];
- if(![[self delegate] isKindOfClass:[CustomQuery class]] || r.length || snippetControlCounter > -1) return;
+ if(![self delegate] || ![[self delegate] isKindOfClass:[CustomQuery class]] || r.length || snippetControlCounter > -1) return;
if(r.location) {
if([[[self textStorage] attribute:kQuote atIndex:r.location-1 effectiveRange:nil] isEqualToString:kQuoteValue])
@@ -539,14 +544,16 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode fuzzyMode:(BOOL)fuzzySearch autoCompleteMode:(BOOL)autoCompleteMode
{
- if(![self isEditable] || (completionIsOpen && !completionWasReinvokedAutomatically)) return;
-
// Cancel autocompletion trigger
if([prefs boolForKey:SPCustomQueryAutoComplete])
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:@selector(doAutoCompletion)
object:nil];
+ if(![self isEditable] || (completionIsOpen && !completionWasReinvokedAutomatically)) {
+ return;
+ }
+
[self breakUndoCoalescing];
NSUInteger caretPos = NSMaxRange([self selectedRange]);
@@ -728,6 +735,12 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
completionIsOpen = YES;
+ // Cancel autocompletion trigger again if user typed something in while parsing
+ if([prefs boolForKey:SPCustomQueryAutoComplete])
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(doAutoCompletion)
+ object:nil];
+
SPNarrowDownCompletion* completionPopUp = [[SPNarrowDownCompletion alloc] initWithItems:[self suggestionsForSQLCompletionWith:currentWord dictMode:isDictMode browseMode:dbBrowseMode withTableName:tableName withDbName:dbName]
alreadyTyped:filter
staticPrefix:prefix
@@ -746,7 +759,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
selectedDb:currentDb
caretMovedLeft:caretMovedLeft
autoComplete:autoCompleteMode
- oneColumn:isDictMode];
+ oneColumn:isDictMode
+ isQueryingDBStructure:[mySQLConnection isQueryingDatabaseStructure]];
completionParseRangeLocation = parseRange.location;
@@ -1275,6 +1289,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
return;
}
+ completionIsOpen = YES;
+
SPNarrowDownCompletion* completionPopUp = [[SPNarrowDownCompletion alloc] initWithItems:possibleCompletions
alreadyTyped:@""
staticPrefix:@""
@@ -1293,7 +1309,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
selectedDb:@""
caretMovedLeft:NO
autoComplete:NO
- oneColumn:NO];
+ oneColumn:NO
+ isQueryingDBStructure:NO];
//Get the NSPoint of the first character of the current word
NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,0) actualCharacterRange:NULL];
@@ -1303,7 +1320,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Adjust list location to be under the current word or insertion point
pos.y -= [[self font] pointSize]*1.25;
[completionPopUp setCaretPos:pos];
- completionIsOpen = YES;
[completionPopUp orderFront:self];
}
@@ -1438,7 +1454,9 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSMutableArray *possibleCompletions = [[[NSMutableArray alloc] initWithCapacity:[list count]] autorelease];
for(id w in list)
[possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:w, @"display", @"dummy-small", @"image", nil]];
-
+
+ completionIsOpen = YES;
+
completionPopUp = [[SPNarrowDownCompletion alloc] initWithItems:possibleCompletions
alreadyTyped:@""
staticPrefix:@""
@@ -1457,7 +1475,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
selectedDb:@""
caretMovedLeft:NO
autoComplete:NO
- oneColumn:YES];
+ oneColumn:YES
+ isQueryingDBStructure:NO];
//Get the NSPoint of the first character of the current word
NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,0) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
@@ -1466,7 +1485,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Adjust list location to be under the current word or insertion point
pos.y -= [[self font] pointSize]*1.25;
[completionPopUp setCaretPos:pos];
- completionIsOpen = YES;
[completionPopUp orderFront:self];
}
}
@@ -3252,6 +3270,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void) dealloc
{
+ if([prefs boolForKey:SPCustomQueryUpdateAutoHelp])
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(autoHelp)
+ object:nil];
+
+ if([prefs boolForKey:SPCustomQueryAutoComplete])
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(doAutoCompletion)
+ object:nil];
+
+
// Remove observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
[prefs removeObserver:self forKeyPath:SPCustomQueryEditorFont];
diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h
index e2aaed5e..fc8bd942 100644
--- a/Source/SPNarrowDownCompletion.h
+++ b/Source/SPNarrowDownCompletion.h
@@ -49,12 +49,19 @@
BOOL commaInsertionMode;
BOOL autoCompletionMode;
BOOL oneColumnMode;
+ BOOL isQueryingDatabaseStructure;
NSInteger backtickMode;
NSFont *tableFont;
NSRange theCharRange;
NSRange theParseRange;
NSString *theDbName;
+ NSTimer *stateTimer;
+ NSArray *syncArrowImages;
+ NSInteger currentSyncImage;
+
+ NSUInteger timeCounter;
+
id theView;
NSInteger maxWindowWidth;
@@ -71,10 +78,15 @@
charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
- selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn;
+ selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
+ isQueryingDBStructure:(BOOL)isQueryingDBStructure;
- (void)setCaretPos:(NSPoint)aPos;
- (void)insert_text:(NSString* )aString;
- (void)insertCommonPrefix;
- (void)adjustWorkingRangeByDelta:(NSInteger)delta;
+- (void)updateSyncArrowStatus;
+- (void)reInvokeCompletion;
+
+
@end
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index cf5abb49..287fe98d 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -39,6 +39,7 @@
#import "CMTextView.h"
#import "SPConstants.h"
+
@interface NSTableView (MovingSelectedRow)
- (BOOL)SP_NarrowDownCompletion_canHandleEvent:(NSEvent*)anEvent;
@@ -113,17 +114,32 @@
caseSensitive = YES;
filtered = nil;
spaceCounter = 0;
-
+ currentSyncImage = 0;
prefs = [NSUserDefaults standardUserDefaults];
tableFont = [NSUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:SPCustomQueryEditorFont]];
[self setupInterface];
+
+ syncArrowImages = [[NSArray alloc] initWithObjects:
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_01" ofType:@"tiff"]],
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_02" ofType:@"tiff"]],
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_03" ofType:@"tiff"]],
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_04" ofType:@"tiff"]],
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_05" ofType:@"tiff"]],
+ [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sync_arrows_06" ofType:@"tiff"]],
+ nil];
+
}
return self;
}
- (void)dealloc
{
+ if(stateTimer != nil) {
+ [stateTimer invalidate];
+ [stateTimer release];
+ }
+ stateTimer = nil;
[staticPrefix release];
[mutablePrefix release];
[textualInputCharacters release];
@@ -135,12 +151,45 @@
[super dealloc];
}
+- (void)updateSyncArrowStatus
+{
+ currentSyncImage++;
+ timeCounter++;
+ if(currentSyncImage >= [syncArrowImages count]) currentSyncImage = 0;
+ if(timeCounter > 20) {
+ timeCounter = 0;
+ if(![[theView valueForKeyPath:@"mySQLConnection"] isQueryingDatabaseStructure]) {
+ isQueryingDatabaseStructure = NO;
+ if(stateTimer) {
+ [stateTimer invalidate];
+ [stateTimer release];
+ stateTimer = nil;
+ if(syncArrowImages) [syncArrowImages release];
+ usleep(1000);
+ [self performSelectorOnMainThread:@selector(reInvokeCompletion) withObject:nil waitUntilDone:NO];
+ return;
+ }
+ }
+ }
+
+ [theTableView setNeedsDisplay:YES];
+
+}
+
+- (void)reInvokeCompletion
+{
+ [theView setCompletionWasReinvokedAutomatically:YES];
+ closeMe = YES;
+ [theView doCompletionByUsingSpellChecker:dictMode fuzzyMode:fuzzyMode autoCompleteMode:NO];
+}
+
- (id)initWithItems:(NSArray*)someSuggestions alreadyTyped:(NSString*)aUserString staticPrefix:(NSString*)aStaticPrefix
additionalWordCharacters:(NSString*)someAdditionalWordCharacters caseSensitive:(BOOL)isCaseSensitive
charRange:(NSRange)initRange parseRange:(NSRange)parseRange inView:(id)aView
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
+ isQueryingDBStructure:(BOOL)isQueryingDBStructure
{
if(self = [self init])
{
@@ -151,6 +200,10 @@
autoCompletionMode = autoComplete;
oneColumnMode = oneColumn;
+ isQueryingDatabaseStructure = isQueryingDBStructure;
+
+ if(isQueryingDatabaseStructure)
+ stateTimer = [[NSTimer scheduledTimerWithTimeInterval:0.07f target:self selector:@selector(updateSyncArrowStatus) userInfo:nil repeats:YES] retain];
fuzzyMode = fuzzySearch;
if(fuzzyMode)
@@ -258,8 +311,8 @@
[theTableView setDelegate:self];
NSTableColumn *column0 = [[[NSTableColumn alloc] initWithIdentifier:@"image"] autorelease];
- [column0 setDataCell:[[ImageAndTextCell new] autorelease]];
- [column0 setEditable:NO];
+ [column0 setDataCell:[NSImageCell new]];
+ // [column0 setEditable:NO];
[theTableView addTableColumn:column0];
[column0 setMinWidth:0];
[column0 setWidth:20];
@@ -301,7 +354,10 @@
- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex mouseLocation:(NSPoint)mouseLocation
{
+ if(isQueryingDatabaseStructure) rowIndex--;
+
if([[aTableColumn identifier] isEqualToString:@"image"]) {
+ if(rowIndex == -1) return NSLocalizedString(@"fetching database structure in progress", @"fetching database structure in progress");
if(!dictMode) {
NSString *imageName = [[filtered objectAtIndex:rowIndex] objectForKey:@"image"];
if([imageName hasPrefix:@"dummy"])
@@ -321,8 +377,10 @@
}
return @"";
} else if([[aTableColumn identifier] isEqualToString:@"name"]) {
+ if(rowIndex == -1) return NSLocalizedString(@"fetching database structure in progress", @"fetching database structure in progress");
return [[filtered objectAtIndex:rowIndex] objectForKey:@"display"];
} else if ([[aTableColumn identifier] isEqualToString:@"list"] || [[aTableColumn identifier] isEqualToString:@"type"]) {
+ if(rowIndex == -1) return NSLocalizedString(@"fetching database structure data in progress", @"fetching database structure data in progress");
if(dictMode) {
return @"";
} else {
@@ -341,6 +399,7 @@
}
} else if ([[aTableColumn identifier] isEqualToString:@"path"]) {
+ if(rowIndex == -1) return NSLocalizedString(@"fetching database structure in progress", @"fetching database structure in progress");
if(dictMode) {
return @"";
} else {
@@ -360,26 +419,43 @@
return @"";
}
+- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
+{
+ if(rowIndex == 0 && isQueryingDatabaseStructure)
+ return NO;
+
+ return YES;
+}
+
+
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
NSImage* image = nil;
NSString* imageName = nil;
+ if(isQueryingDatabaseStructure) rowIndex--;
+
if([[aTableColumn identifier] isEqualToString:@"image"]) {
if(!dictMode) {
- imageName = [[filtered objectAtIndex:rowIndex] objectForKey:@"image"];
- if(imageName)
- image = [NSImage imageNamed:imageName];
- [[aTableColumn dataCell] setImage:image];
+ if(rowIndex == -1) {
+ return [syncArrowImages objectAtIndex:currentSyncImage];
+ } else {
+ imageName = [[filtered objectAtIndex:rowIndex] objectForKey:@"image"];
+ if(imageName)
+ image = [NSImage imageNamed:imageName];
+ return image;
+ }
}
return @"";
} else if([[aTableColumn identifier] isEqualToString:@"name"]) {
- // NSTextFieldCell *b = [[[NSTextFieldCell new] initTextCell:[[filtered objectAtIndex:rowIndex] objectForKey:@"display"]] autorelease];
+ if(rowIndex == -1) return @"fetching structure…";
+
[[aTableColumn dataCell] setFont:[NSFont systemFontOfSize:12]];
return [[filtered objectAtIndex:rowIndex] objectForKey:@"display"];
} else if ([[aTableColumn identifier] isEqualToString:@"list"]) {
+ if(rowIndex == -1) return @"";
if(dictMode) {
return @"";
} else {
@@ -407,11 +483,10 @@
}
} else if([[aTableColumn identifier] isEqualToString:@"type"]) {
+ if(rowIndex == -1) return @"";
if(dictMode) {
return @"";
} else {
- // [[aTableColumn dataCell] setTextColor:([aTableView selectedRow] == rowIndex)?[NSColor whiteColor]:[NSColor darkGrayColor]];
- // return ([[filtered objectAtIndex:rowIndex] objectForKey:@"type"]) ? [[filtered objectAtIndex:rowIndex] objectForKey:@"type"] : @"";
NSTokenFieldCell *b = [[[NSTokenFieldCell alloc] initTextCell:([[filtered objectAtIndex:rowIndex] objectForKey:@"type"]) ? [[filtered objectAtIndex:rowIndex] objectForKey:@"type"] : @""] autorelease];
[b setEditable:NO];
[b setAlignment:NSRightTextAlignment];
@@ -421,6 +496,7 @@
}
} else if ([[aTableColumn identifier] isEqualToString:@"path"]) {
+ if(rowIndex == -1) return @"";
if(dictMode) {
return @"";
} else {
@@ -461,7 +537,7 @@
if(autoCompletionMode) return;
if(spaceCounter < 1)
for(id w in filtered){
- if([[w objectForKey:@"match"] ?: [w objectForKey:@"display"] rangeOfString:@" "].length) {
+ if([[w objectForKey:@"match"] ?: [w objectForKey:@"display"] rangeOfString:@" "].length && ![w objectForKey:@"noCompletion"]) {
[textualInputCharacters addCharactersInString:@" "];
break;
}
@@ -615,6 +691,9 @@
- (void)watchUserEvents
{
+
+ [theView setCompletionIsOpen:YES];
+
closeMe = NO;
while(!closeMe)
{
@@ -797,9 +876,22 @@
{
if([theTableView selectedRow] == -1) return;
- NSDictionary* selectedItem = [filtered objectAtIndex:[theTableView selectedRow]];
+ NSDictionary* selectedItem;
+ NSInteger selectedRowNumber = [theTableView selectedRow];
+ if(isQueryingDatabaseStructure) {
+ if(selectedRowNumber < 1) {
+ closeMe = YES;
+ return;
+ }
+ selectedItem = [filtered objectAtIndex:selectedRowNumber-1];
+ } else {
+ selectedItem = [filtered objectAtIndex:selectedRowNumber];
+ }
- if([selectedItem objectForKey:@"noCompletion"]) return;
+ if([selectedItem objectForKey:@"noCompletion"]) {
+ closeMe = YES;
+ return;
+ }
if(dictMode){
[self insert_text:[selectedItem objectForKey:@"match"] ?: [selectedItem objectForKey:@"display"]];
diff --git a/Source/SPNavigatorController.h b/Source/SPNavigatorController.h
index 6c31b013..2833cd99 100644
--- a/Source/SPNavigatorController.h
+++ b/Source/SPNavigatorController.h
@@ -45,7 +45,7 @@
NSMutableDictionary *schemaDataFiltered;
NSMutableDictionary *allSchemaKeys;
NSMutableArray *infoArray;
- NSMutableSet *updatingConnections;
+ NSMutableArray *updatingConnections;
NSMutableDictionary *expandStatus1;
NSMutableDictionary *expandStatus2;
@@ -80,13 +80,13 @@
- (NSString*)tableInfoLabelForIndex:(NSInteger)index ofType:(NSInteger)type;
- (void)updateNavigator:(NSNotification *)aNotification;
-- (void)isUpdatingNavigator:(NSNotification *)aNotification;
- (NSDictionary *)dbStructureForConnection:(NSString*)connectionID;
- (NSArray *)allSchemaKeysForConnection:(NSString*)connectionID;
- (NSArray *)getUniqueDbIdentifierFor:(NSString*)term andConnection:(NSString*)connectionID;
- (BOOL)isUpdatingConnection:(NSString*)connectionID;
+- (BOOL)isUpdating;
- (void)restoreSelectedItems;
- (void)setIgnoreUpdate:(BOOL)flag;
diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m
index 439b4d5c..0bb723ba 100644
--- a/Source/SPNavigatorController.m
+++ b/Source/SPNavigatorController.m
@@ -70,7 +70,7 @@ static SPNavigatorController *sharedNavigatorController = nil;
expandStatus1 = [[NSMutableDictionary alloc] init];
expandStatus2 = [[NSMutableDictionary alloc] init];
infoArray = [[NSMutableArray alloc] init];
- updatingConnections = [[NSMutableSet alloc] initWithCapacity:1];
+ updatingConnections = [[NSMutableArray alloc] init];
selectedKey1 = @"";
selectedKey2 = @"";
ignoreUpdate = NO;
@@ -143,9 +143,6 @@ static SPNavigatorController *sharedNavigatorController = nil;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateNavigator:)
name:@"SPDBStructureWasUpdated" object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(isUpdatingNavigator:)
- name:@"SPDBStructureIsUpdating" object:nil];
-
}
- (NSString *)windowFrameAutosaveName
@@ -349,12 +346,14 @@ static SPNavigatorController *sharedNavigatorController = nil;
[schemaData removeObjectForKey:connectionID];
if(allSchemaKeys)
[allSchemaKeys removeObjectForKey:connectionID];
-
- [outlineSchema1 reloadData];
- [outlineSchema2 reloadData];
- [self restoreSelectedItems];
- if(isFiltered)
- [self filterTree:self];
+
+ if([[self window] isVisible]) {
+ [outlineSchema1 reloadData];
+ [outlineSchema2 reloadData];
+ [self restoreSelectedItems];
+ if(isFiltered)
+ [self filterTree:self];
+ }
}
}
@@ -418,15 +417,6 @@ static SPNavigatorController *sharedNavigatorController = nil;
[self performSelectorOnMainThread:@selector(updateEntriesForConnection:) withObject:nil waitUntilDone:YES];
}
-- (void)isUpdatingNavigator:(NSNotification *)aNotification
-{
- // id object = [aNotification object];
- //
- // if([object isKindOfClass:[TableDocument class]])
- // [updatingConnections addObject:[object connectionID]];
-
-}
-
- (void)updateEntriesForConnection:(NSString*)connectionID
{
@@ -435,13 +425,16 @@ static SPNavigatorController *sharedNavigatorController = nil;
return;
}
- [self saveSelectedItems];
+ if([[self window] isVisible]) {
+ [self saveSelectedItems];
+ [infoArray removeAllObjects];
+ }
- [infoArray removeAllObjects];
+ id doc = nil;
if ([[[NSDocumentController sharedDocumentController] documents] count]) {
- id doc = [[NSDocumentController sharedDocumentController] currentDocument];
+ doc = [[NSDocumentController sharedDocumentController] currentDocument];
id theConnection = [doc valueForKeyPath:@"mySQLConnection"];
if(!theConnection || ![theConnection isConnected]) return;
@@ -449,7 +442,6 @@ static SPNavigatorController *sharedNavigatorController = nil;
NSString *connectionName = [doc connectionID];
if(!connectionName || [connectionName isEqualToString:@"_"] || (connectionID && ![connectionName isEqualToString:connectionID]) ) {
-// NSLog(@"navigator update skipped %@", connectionName);
return;
}
@@ -481,21 +473,27 @@ static SPNavigatorController *sharedNavigatorController = nil;
[allSchemaKeys setObject:[NSArray array] forKey:connectionName];
}
- [outlineSchema1 reloadData];
- [outlineSchema2 reloadData];
+ [updatingConnections removeObject:connectionName];
- [self restoreExpandStatus];
- [self restoreSelectedItems];
+ if([[self window] isVisible]) {
+ [outlineSchema1 reloadData];
+ [outlineSchema2 reloadData];
- [updatingConnections removeObject:connectionName];
+ [self restoreExpandStatus];
+ [self restoreSelectedItems];
+ }
}
- [self syncButtonAction:self];
+ if([[self window] isVisible])
+ [self syncButtonAction:self];
if(isFiltered && [[self window] isVisible])
[self filterTree:self];
-
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"SPNavigatorStructureWasUpdated" object:doc];
+
+
}
- (BOOL)schemaPathExistsForConnection:(NSString*)connectionID andDatabase:(NSString*)dbname
@@ -575,6 +573,11 @@ static SPNavigatorController *sharedNavigatorController = nil;
return ([updatingConnections containsObject:connectionID]) ? YES : NO;
}
+- (BOOL)isUpdating
+{
+ return ([updatingConnections count]) ? YES : NO;
+}
+
#pragma mark -
#pragma mark IBActions
@@ -824,6 +827,7 @@ static SPNavigatorController *sharedNavigatorController = nil;
return [item objectAtIndex:index];
}
return nil;
+
}
- (BOOL)outlineView:(id)outlineView isItemExpandable:(id)item
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index 4ef781e3..b2da29af 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -3690,9 +3690,8 @@
*/
- (void)windowWillClose:(NSNotification *)aNotification
{
- if ([[[SPNavigatorController sharedNavigatorController] window] isVisible]) {
- [[SPNavigatorController sharedNavigatorController] removeConnection:[self connectionID]];
- }
+
+ [[SPNavigatorController sharedNavigatorController] removeConnection:[self connectionID]];
[mySQLConnection setDelegate:nil];
if (_isConnected) [self closeConnection];