aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/MCPKit
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/MCPKit')
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m500
-rw-r--r--Frameworks/MCPKit/Support files/NSNotificationAdditions.h35
-rw-r--r--Frameworks/MCPKit/Support files/NSNotificationAdditions.m76
3 files changed, 352 insertions, 259 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
index c24c8c8f..d5e11445 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
@@ -34,6 +34,7 @@
#import "MCPConnectionProxy.h"
#import "MCPStringAdditions.h"
#import "RegexKitLite.h"
+#import "NSNotificationAdditions.h"
#include <unistd.h>
#include <mach/mach_time.h>
@@ -1376,7 +1377,7 @@ void performThreadedKeepAlive(void *ptr)
if ([delegate respondsToSelector:@selector(queryGaveError:connection:)]) [delegate queryGaveError:@"No connection available!" connection:self];
// Notify that the query has been performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:delegate];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:delegate];
// Inform the delegate that there is no connection available
if (delegate && [delegate respondsToSelector:@selector(noConnectionAvailable:)]) {
@@ -1427,11 +1428,14 @@ void performThreadedKeepAlive(void *ptr)
[self setLastErrorMessage:errorMessage];
// Notify that the query has been performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:delegate];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:delegate];
+
// Show an error alert while resetting
- NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"),
- nil, nil, [delegate valueForKeyPath:@"tableWindow"], self, nil, nil, nil, errorMessage);
-
+ if ([delegate respondsToSelector:@selector(showErrorWithTitle:message:)])
+ [delegate showErrorWithTitle:NSLocalizedString(@"Error", @"error") message:errorMessage];
+ else
+ NSRunAlertPanel(NSLocalizedString(@"Error", @"error"), errorMessage, @"OK", nil, nil);
+
return nil;
}
}
@@ -1445,7 +1449,7 @@ void performThreadedKeepAlive(void *ptr)
if (queryErrorMessage) [queryErrorMessage release], queryErrorMessage = nil;
// Notify that the query has been performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:delegate];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:delegate];
return nil;
}
}
@@ -1483,7 +1487,8 @@ void performThreadedKeepAlive(void *ptr)
// Ensure no problem occurred during the result fetch
if (mysql_errno(mConnection) != 0) {
- queryErrorMessage = [[NSString alloc] initWithString:[self stringWithCString:mysql_error(mConnection)]];
+ queryErrorMessage = [self stringWithCString:mysql_error(mConnection)];
+ if (queryErrorMessage) [queryErrorMessage retain];
queryErrorId = mysql_errno(mConnection);
break;
}
@@ -1511,7 +1516,8 @@ void performThreadedKeepAlive(void *ptr)
queryErrorId = 1317;
} else {
if (queryErrorMessage) [queryErrorMessage release], queryErrorMessage = nil;
- queryErrorMessage = [[NSString alloc] initWithString:[self stringWithCString:mysql_error(mConnection)]];
+ queryErrorMessage = [self stringWithCString:mysql_error(mConnection)];
+ if (queryErrorMessage) [queryErrorMessage retain];
queryErrorId = mysql_errno(mConnection);
// If the error was a connection error, retry once
@@ -1600,55 +1606,58 @@ void performThreadedKeepAlive(void *ptr)
// Set queryCancelled to prevent query retries
queryCancelled = YES;
- // For MySQL server versions >=5, try to kill the connection. This requires
- // setting up a new connection, and running a KILL QUERY via it.
- if ([self serverMajorVersion] >= 5) {
-
- MYSQL *killerConnection = mysql_init(NULL);
- if (killerConnection) {
- const char *theLogin = [self cStringFromString:connectionLogin];
- const char *theHost;
- const char *thePass = NULL;
- const char *theSocket;
- void *connectionSetupStatus;
+ // Set up a new connection, and running a KILL QUERY via it.
+ MYSQL *killerConnection = mysql_init(NULL);
+ if (killerConnection) {
+ const char *theLogin = [self cStringFromString:connectionLogin];
+ const char *theHost;
+ const char *thePass = NULL;
+ const char *theSocket;
+ void *connectionSetupStatus;
- mysql_options(killerConnection, MYSQL_OPT_CONNECT_TIMEOUT, (const void *)&connectionTimeout);
+ mysql_options(killerConnection, 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];
+ // 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]];
}
- if (connectionSocket == nil || ![connectionSocket length]) {
- theSocket = kMCPConnectionDefaultSocket;
+ } else {
+ thePass = [self cStringFromString:connectionPassword];
+ }
+
+ // Connect
+ connectionSetupStatus = mysql_real_connect(killerConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
+ thePass = NULL;
+ if (connectionSetupStatus) {
+
+ // Set up a KILL query. For MySQL 5+, kill just the query; otherwise, kill the thread.
+ NSStringEncoding killerConnectionEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(killerConnection)];
+ NSString *killQueryString;
+ if ([self serverMajorVersion] >= 5) {
+ killQueryString = [NSString stringWithFormat:@"KILL QUERY %lu", mConnection->thread_id];
} else {
- theSocket = [self cStringFromString:connectionSocket];
+ killQueryString = [NSString stringWithFormat:@"KILL %lu", mConnection->thread_id];
}
- if (!connectionPassword) {
- if (delegate && [delegate respondsToSelector:@selector(keychainPasswordForConnection:)]) {
- thePass = [self cStringFromString:[delegate keychainPasswordForConnection:self]];
- }
- } else {
- thePass = [self cStringFromString:connectionPassword];
- }
-
- // Connect
- connectionSetupStatus = mysql_real_connect(killerConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
- thePass = NULL;
- if (connectionSetupStatus) {
- NSStringEncoding killerConnectionEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(killerConnection)];
- NSString *killerQueryString = [NSString stringWithFormat:@"KILL QUERY %lu", mConnection->thread_id];
- NSData *encodedKillerQueryData = NSStringDataUsingLossyEncoding(killerQueryString, killerConnectionEncoding, 1);
- const char *killerQueryCString = [encodedKillerQueryData bytes];
- unsigned long killerQueryCStringLength = [encodedKillerQueryData length];
- if (mysql_real_query(killerConnection, killerQueryCString, killerQueryCStringLength) == 0) {
- mysql_close(killerConnection);
- queryCancelUsedReconnect = NO;
- return;
- }
+ NSData *encodedKillQueryData = NSStringDataUsingLossyEncoding(killQueryString, killerConnectionEncoding, 1);
+ const char *killQueryCString = [encodedKillQueryData bytes];
+ unsigned long killQueryCStringLength = [encodedKillQueryData length];
+ if (mysql_real_query(killerConnection, killQueryCString, killQueryCStringLength) == 0) {
mysql_close(killerConnection);
+ queryCancelUsedReconnect = NO;
+ return;
}
+ mysql_close(killerConnection);
}
}
@@ -1877,6 +1886,7 @@ void performThreadedKeepAlive(void *ptr)
- (void)queryDbStructureWithUserInfo:(NSDictionary*)userInfo
{
NSAutoreleasePool *queryPool = [[NSAutoreleasePool alloc] init];
+ BOOL structureWasUpdated = NO;
// if 'cancelQuerying' is set try to interrupt any current querying
if(userInfo && [userInfo objectForKey:@"cancelQuerying"])
@@ -1900,37 +1910,35 @@ void performThreadedKeepAlive(void *ptr)
// Re-init with already cached data from navigator controller
NSMutableDictionary *queriedStructure = [NSMutableDictionary dictionary];
NSDictionary *dbstructure = [[self delegate] getDbStructure];
- [queriedStructure setDictionary:[NSMutableDictionary dictionaryWithDictionary:dbstructure]];
+ if (dbstructure) [queriedStructure setDictionary:[NSMutableDictionary dictionaryWithDictionary:dbstructure]];
NSMutableArray *queriedStructureKeys = [NSMutableArray array];
NSArray *dbStructureKeys = [[self delegate] allSchemaKeys];
- [queriedStructureKeys setArray:dbStructureKeys];
+ if (dbStructureKeys) [queriedStructureKeys setArray:dbStructureKeys];
- BOOL removeAddFlag = NO;
+ // Retrieve all the databases known of by the delegate
+ NSMutableArray *connectionDatabases = [NSMutableArray array];
+ [connectionDatabases addObjectsFromArray:[[self delegate] allSystemDatabaseNames]];
+ [connectionDatabases addObjectsFromArray:[[self delegate] allDatabaseNames]];
// 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) {
+ for (id db in connectionDatabases) {
NSString *dbid = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db];
if(![queriedStructure objectForKey:dbid]) {
- removeAddFlag = YES;
+ structureWasUpdated = YES;
[queriedStructure setObject:db forKey:dbid];
[queriedStructureKeys addObject:dbid];
}
}
- // Remove deleted databases in structure and keys in allKeysofDbStructure
- // Use a dict to avoid <NSCFDictionary> was mutated while being enumerated. while iterating via allKeys
+ // Check the existing databases in the 'structure' and 'allKeysOfDbStructure' stores,
+ // and remove any that are no longer found in the connectionDatabases list (indicating deletion).
+ // Iterate through extracted keys to avoid <NSCFDictionary> mutation while being enumerated.
NSArray *keys = [queriedStructure allKeys];
for(id key in keys) {
NSString *db = [[key componentsSeparatedByString:SPUniqueSchemaDelimiter] objectAtIndex:1];
- if(![dbs containsObject:db]) {
- removeAddFlag = YES;
+ if(![connectionDatabases containsObject:db]) {
+ structureWasUpdated = YES;
[queriedStructure removeObjectForKey:key];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", key, SPUniqueSchemaDelimiter]];
[queriedStructureKeys filterUsingPredicate:predicate];
@@ -1942,79 +1950,73 @@ void performThreadedKeepAlive(void *ptr)
if([delegate respondsToSelector:@selector(database)])
currentDatabase = [[self delegate] database];
+ // Determine whether the database details need to be queried.
+ BOOL shouldQueryStructure = YES;
+ NSString *db_id = nil;
+
+ // If no database is selected, no need to check further
if(!currentDatabase || (currentDatabase && ![currentDatabase length])) {
+ shouldQueryStructure = NO;
+
+ // Otherwise, build up the schema key for the database to be retrieved.
+ } else {
+ db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, currentDatabase];
+
+ // Check to see if a cache already exists for the database.
+ if ([queriedStructure objectForKey:db_id] && [[queriedStructure objectForKey:db_id] isKindOfClass:[NSDictionary class]]) {
+
+ // The cache is available. If the `mysql` or `information_schema` databases are being queried,
+ // never requery as their structure will never change.
+ if ([currentDatabase isEqualToString:@"mysql"] || [currentDatabase isEqualToString:@"information_schema"]) {
+ shouldQueryStructure = NO;
+
+ // Otherwise, if the forceUpdate flag wasn't supplied or evaluates to false, also don't update.
+ } else if (userInfo == nil || ![userInfo objectForKey:@"forceUpdate"] || ![[userInfo objectForKey:@"forceUpdate"] boolValue]) {
+ shouldQueryStructure = NO;
+ }
+ }
+ }
+
+ // If it has been determined that no new structure needs to be retrieved, clean up and return.
+ if (!shouldQueryStructure) {
- // Updating the global variables and make sure that no request reads these global variables
+ // Update 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, @"structure", queriedStructureKeys, @"keys", nil] waitUntilDone:YES];
[self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
- if(removeAddFlag)
+ if (structureWasUpdated)
[[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
[queryPool release];
return;
}
- NSString *db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, currentDatabase];
-
- // 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]]) {
-
- // 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, @"structure", queriedStructureKeys, @"keys", nil] waitUntilDone:YES];
- [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
- }
- 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, @"structure", queriedStructureKeys, @"keys", nil] waitUntilDone:YES];
- [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
+ // Retrieve the tables and views for this database from tablesList (TODO: split out of MCPKit)
+ NSMutableArray *tablesAndViews = [NSMutableArray array];
+ for (id aTable in [[[self delegate] valueForKeyPath:@"tablesListInstance"] allTableNames]) {
+ NSDictionary *aTableDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ aTable, @"name",
+ @"0", @"type",
+ nil];
+ [tablesAndViews addObject:aTableDict];
}
-
- 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, @"structure", queriedStructureKeys, @"keys", nil] waitUntilDone:YES];
- [self performSelectorOnMainThread:@selector(unlockQuerying) withObject:nil waitUntilDone:YES];
- if(removeAddFlag)
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SPDBStructureWasUpdated" object:delegate];
- [queryPool release];
- return;
- }
+ for (id aView in [[[self delegate] valueForKeyPath:@"tablesListInstance"] allViewNames]) {
+ NSDictionary *aViewDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ aView, @"name",
+ @"0", @"type",
+ nil];
+ [tablesAndViews addObject:aViewDict];
}
- 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];
-
// Do not parse more than 2000 tables/views per db
- if(numberOfTables > 2000) {
- NSLog(@"%ld items in database %@. Only 2000 items can be parsed. Stopped parsing.", numberOfTables, currentDatabase);
- [queryPool release];
- return;
- }
+ if([tablesAndViews count] > 2000) {
+ NSLog(@"%ld items in database %@. Only 2000 items can be parsed. Stopped parsing.", [tablesAndViews count], currentDatabase);
+ [queryPool release];
+ return;
+ }
- // For future usage
+ // For future usage - currently unused
+ // If the affected item name and type - for example, table type and table name - were supplied, extract it.
NSString *affectedItem = nil;
NSInteger affectedItemType = -1;
if(userInfo && [userInfo objectForKey:@"affectedItem"]) {
@@ -2025,15 +2027,14 @@ void performThreadedKeepAlive(void *ptr)
affectedItem = nil;
}
- // Delete all stored data for to be queried db
- if([queriedStructure isKindOfClass:[NSDictionary class]])
- [queriedStructure removeObjectForKey:db_id];
+ // Delete all stored data for the database to be updated, leaving the structure key
+ [queriedStructure removeObjectForKey:db_id];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT SELF BEGINSWITH %@", [NSString stringWithFormat:@"%@%@", db_id, SPUniqueSchemaDelimiter]];
[queriedStructureKeys filterUsingPredicate:predicate];
- [queriedStructureKeys removeObject:db_id];
- // Re-add currentDatabase in case that structure querying will fail
- [queriedStructure setObject:currentDatabase forKey:db_id];
+ // Set up the database as an empty mutable dictionary ready for tables, and store a reference
+ [queriedStructure setObject:[NSMutableDictionary dictionary] forKey:db_id];
+ NSMutableDictionary *databaseStructure = [queriedStructure objectForKey:db_id];
NSString *currentDatabaseEscaped = [currentDatabase stringByReplacingOccurrencesOfString:@"`" withString:@"``"];
@@ -2076,105 +2077,68 @@ void performThreadedKeepAlive(void *ptr)
NSString *charset;
NSUInteger uniqueCounter = 0; // used to make field data unique
-
- // Get the doc encoding due to pref settings etc.
- NSString *docEncoding = [[self delegate] connectionEncoding];
- NSStringEncoding theConnectionEncoding = [MCPConnection encodingForMySQLEncoding:[self cStringFromString:docEncoding]];
-
- // Try to set connection encoding
- NSString *query = [NSString stringWithFormat:@"SET NAMES '%@'", docEncoding];
- 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 while querying the database structure. Could not set encoding to %@", docEncoding);
- [queryPool release];
- return;
- }
+ NSString *query;
+ NSData *encodedQueryData;
+ const char *queryCString;
+ unsigned long queryCStringLength;
+
+ // Get the doc encoding due to pref settings etc, defaulting to UTF8
+ NSString *docEncoding = [[self delegate] connectionEncoding];
+ if (!docEncoding) docEncoding = @"utf8";
+ NSStringEncoding theConnectionEncoding = [MCPConnection encodingForMySQLEncoding:[self cStringFromString:docEncoding]];
+
+ // Try to set connection encoding for MySQL >= 4.1
+ if ([self serverMajorVersion] > 4 || ([self serverMajorVersion] >= 4 && [self serverMinorVersion] >= 1)) {
+ query = [NSString stringWithFormat:@"SET NAMES '%@'", docEncoding];
+ encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
+ queryCString = [encodedQueryData bytes];
+ queryCStringLength = [encodedQueryData length];
+ if (mysql_real_query(structConnection, queryCString, queryCStringLength) != 0) {
+ NSLog(@"Error while querying the database structure. Could not set encoding to %@", docEncoding);
+ [queryPool release];
+ return;
+ }
+ }
// Increase global query-db-counter
[self performSelectorOnMainThread:@selector(incrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
- [queriedStructureKeys addObject:db_id];
+ // Loop through the known tables and views, retrieving details for each
+ for (NSDictionary *aTableDict in tablesAndViews) {
- // Query all tables
- for(NSString* table in tables) {
- if(cancelQueryingDbStructure) {
+ // If cancelled, abort without saving
+ if (cancelQueryingDbStructure) {
[self performSelectorOnMainThread:@selector(decrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
[queryPool release];
return;
}
- 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 stringWithCString:row[0] usingEncoding:theConnectionEncoding] ;
- NSString *type = [self stringWithCString:row[1] usingEncoding:theConnectionEncoding] ;
- NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
- NSString *coll = [self stringWithCString:row[2] usingEncoding:theConnectionEncoding] ;
- NSString *isnull = [self stringWithCString:row[3] usingEncoding:theConnectionEncoding] ;
- NSString *key = [self stringWithCString:row[4] usingEncoding:theConnectionEncoding] ;
- NSString *def = [self stringWithCString:row[5] usingEncoding:theConnectionEncoding] ;
- NSString *extra = [self stringWithCString:row[6] usingEncoding:theConnectionEncoding] ;
- NSString *priv = [self stringWithCString:row[7] usingEncoding:theConnectionEncoding] ;
- NSString *comment = [self stringWithCString:row[8] usingEncoding:theConnectionEncoding] ;
- NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
- NSArray *a = [coll componentsSeparatedByString:@"_"];
- charset = ([a count]) ? [a objectAtIndex:0] : @"";
+ // Extract the name
+ NSString *aTableName = [aTableDict objectForKey:@"name"];
- [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) {
- if(cancelQueryingDbStructure) {
- [self performSelectorOnMainThread:@selector(decrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
- [queryPool release];
- return;
- }
+ // Retrieve the column details
NSString *query = [NSString stringWithFormat:@"SHOW FULL COLUMNS FROM `%@` FROM `%@`",
- [table stringByReplacingOccurrencesOfString:@"`" withString:@"``"],
+ [aTableName 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);
+ // NSLog(@"error %@", aTableName);
continue;
}
-
theResult = mysql_use_result(structConnection);
- NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
+ // Add a structure key for this table
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, aTableName];
[queriedStructureKeys addObject:table_id];
- NSString *charset;
- while(row = mysql_fetch_row(theResult)) {
+ // Add a mutable dictionary to the structure and store a reference
+ [databaseStructure setObject:[NSMutableDictionary dictionary] forKey:table_id];
+ NSMutableDictionary *tableStructure = [databaseStructure objectForKey:table_id];
+
+ // Loop through the fields, extracting details for each
+ while (row = mysql_fetch_row(theResult)) {
NSString *field = [self stringWithCString:row[0] usingEncoding:theConnectionEncoding] ;
NSString *type = [self stringWithCString:row[1] usingEncoding:theConnectionEncoding] ;
NSString *type_display = [type stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"];
@@ -2184,80 +2148,88 @@ void performThreadedKeepAlive(void *ptr)
NSString *def = [self stringWithCString:row[5] usingEncoding:theConnectionEncoding] ;
NSString *extra = [self stringWithCString:row[6] usingEncoding:theConnectionEncoding] ;
NSString *priv = [self stringWithCString:row[7] usingEncoding:theConnectionEncoding] ;
- NSString *comment = [self stringWithCString:row[8] usingEncoding:theConnectionEncoding] ;
- NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
+ NSString *comment;
+ if (sizeof(row) > 8) {
+ comment = [self stringWithCString:row[8] usingEncoding:theConnectionEncoding] ;
+ } else {
+ comment = @"";
+ }
NSArray *a = [coll componentsSeparatedByString:@"_"];
charset = ([a count]) ? [a objectAtIndex:0] : @"";
+ // Add a structure key for this field
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
[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 "];
+
+ [tableStructure setObject:[NSArray arrayWithObjects:type, def, isnull, charset, coll, key, extra, priv, comment, type_display, [NSNumber numberWithUnsignedLongLong:uniqueCounter], nil] forKey:field_id];
+ [tableStructure setObject:[aTableDict objectForKey:@"type"] forKey:@" struct_type "];
uniqueCounter++;
}
mysql_free_result(theResult);
usleep(10);
}
+ // If the MySQL version is higher than 5, also retrieve function/procedure details via the information_schema table
if([self serverMajorVersion] >= 5) {
- // information_schema is UTF-8 encoded
+
+ // The information_schema table is UTF-8 encoded - alter the connection
query = @"SET NAMES 'utf8'";
encodedQueryData = NSStringDataUsingLossyEncoding(query, theConnectionEncoding, 1);
queryCString = [encodedQueryData bytes];
queryCStringLength = [encodedQueryData length];
if (mysql_real_query(structConnection, queryCString, queryCStringLength) == 0) {
+
// 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)) {
+ 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);
+
+ // Loop through the rows and extract the function details
+ while(row = mysql_fetch_row(theResult)) {
+
+ // If cancelled, abort without saving the new structure
if(cancelQueryingDbStructure) {
[self performSelectorOnMainThread:@selector(decrementQueryingDbStructure) withObject:nil waitUntilDone:YES];
[queryPool release];
return;
}
- 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]];
-
- [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);
- } else {
- NSLog(@"Error while querying the database structure for procedures and functions. Could not set encoding to utf8");
- }
+
+ NSString *fname = [self stringWithUTF8CString:row[0]];
+ 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]];
+
+ // Generate "table" and "field" names and add to structure key store
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, fname];
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, fname];
+ [queriedStructureKeys addObject:table_id];
+ [queriedStructureKeys addObject:field_id];
+
+ // Ensure that a dictionary exists for this "table" name
+ if(![[queriedStructure valueForKey:db_id] valueForKey:table_id])
+ [[queriedStructure valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
+
+ // Add the "field" details
+ [[[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);
+ } else {
+ NSLog(@"Error while querying the database structure for procedures and functions. Could not set encoding to utf8");
+ }
}
}
- // Updating the global variables and make sure that no request reads these global variables
+ // Update 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, @"structure", queriedStructureKeys, @"keys", nil] waitUntilDone:YES];
@@ -2282,7 +2254,10 @@ void performThreadedKeepAlive(void *ptr)
- (void)updateGlobalVariablesWith:(NSDictionary*)object
{
NSString *connectionID = [[self delegate] connectionID];
+
+ // Return if the delegate indicates disconnection
if([connectionID length] < 2) return;
+
if(![structure valueForKey:connectionID])
[structure setObject:[NSMutableDictionary dictionary] forKey:connectionID];
[structure setObject:[object objectForKey:@"structure"] forKey:connectionID];
@@ -2420,14 +2395,16 @@ void performThreadedKeepAlive(void *ptr)
NSArray *possibleSocketLocations = [NSArray arrayWithObjects:
@"/tmp/mysql.sock", // Default
+ @"/Applications/MAMP/tmp/mysql/mysql.sock", // MAMP default location
+ @"/Applications/xampp/xamppfiles/var/mysql/mysql.sock", // XAMPP default location
+ @"/var/mysql/mysql.sock", // Mac OS X Server default
+ @"/opt/local/var/run/mysqld/mysqld.sock", // Darwinports MySQL
+ @"/opt/local/var/run/mysql4/mysqld.sock", // Darwinports MySQL 4
+ @"/opt/local/var/run/mysql5/mysqld.sock", // Darwinports MySQL 5
@"/var/run/mysqld/mysqld.sock", // As used on Debian/Gentoo
@"/var/tmp/mysql.sock", // As used on FreeBSD
@"/var/lib/mysql/mysql.sock", // As used by Fedora
@"/opt/local/lib/mysql/mysql.sock", // Alternate fedora
- @"/opt/local/var/run/mysqld/mysqld.sock", // Darwinports MySQL
- @"/opt/local/var/run/mysql4/mysqld.sock", // Darwinports MySQL 4
- @"/opt/local/var/run/mysql5/mysqld.sock", // Darwinports MySQL 5
- @"/Applications/MAMP/tmp/mysql/mysql.sock", // MAMP default location
nil];
for (NSInteger i = 0; i < [possibleSocketLocations count]; i++)
@@ -2583,8 +2560,13 @@ void performThreadedKeepAlive(void *ptr)
MCPResult *r;
r = [self queryString:@"SELECT @@global.max_allowed_packet" usingEncoding:mEncoding streamingResult:NO];
if (![[self getLastErrorMessage] isEqualToString:@""]) {
- if ([self isConnected])
- NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while retrieving max_allowed_packet size:\n\n%@", [self getLastErrorMessage]], @"OK", nil, nil);
+ if ([self isConnected]) {
+ NSString *errorMessage = [NSString stringWithFormat:@"An error occured while retrieving max_allowed_packet size:\n\n%@", [self getLastErrorMessage]];
+ if ([delegate respondsToSelector:@selector(showErrorWithTitle:message:)])
+ [delegate showErrorWithTitle:NSLocalizedString(@"Error", @"error") message:errorMessage];
+ else
+ NSRunAlertPanel(@"Error", errorMessage, @"OK", nil, nil);
+ }
return -1;
}
NSArray *a = [r fetchRowAsArray];
diff --git a/Frameworks/MCPKit/Support files/NSNotificationAdditions.h b/Frameworks/MCPKit/Support files/NSNotificationAdditions.h
new file mode 100644
index 00000000..33c8ec10
--- /dev/null
+++ b/Frameworks/MCPKit/Support files/NSNotificationAdditions.h
@@ -0,0 +1,35 @@
+//
+// $Id: NSNotificationAdditions.h 2045 2010-03-31 18:01:50Z stuart02 $
+//
+// NSNotificationAdditions.h
+// sequel-pro
+//
+// Copied from the Colloquy project; original code available from Trac at
+// http://colloquy.info/project/browser/trunk/Additions/NSNotificationAdditions.h
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+@interface NSNotificationCenter (NSNotificationCenterAdditions)
+
+- (void) postNotificationOnMainThread:(NSNotification *) notification;
+- (void) postNotificationOnMainThread:(NSNotification *) notification waitUntilDone:(BOOL) wait;
+
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object;
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo;
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo waitUntilDone:(BOOL) wait;
+
+@end
diff --git a/Frameworks/MCPKit/Support files/NSNotificationAdditions.m b/Frameworks/MCPKit/Support files/NSNotificationAdditions.m
new file mode 100644
index 00000000..f27a4d5c
--- /dev/null
+++ b/Frameworks/MCPKit/Support files/NSNotificationAdditions.m
@@ -0,0 +1,76 @@
+//
+// $Id: NSNotificationAdditions.m 2045 2010-03-31 18:01:50Z stuart02 $
+//
+// NSNotificationAdditions.m
+// sequel-pro
+//
+// Copied from the Colloquy project; original code available from Trac at
+// http://colloquy.info/project/browser/trunk/Additions/NSNotificationAdditions.m
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "NSNotificationAdditions.h"
+#import <pthread.h>
+
+@implementation NSNotificationCenter (NSNotificationCenterAdditions)
+
+- (void) postNotificationOnMainThread:(NSNotification *) notification {
+ if( pthread_main_np() ) return [self postNotification:notification];
+ [self postNotificationOnMainThread:notification waitUntilDone:NO];
+}
+
+- (void) postNotificationOnMainThread:(NSNotification *) notification waitUntilDone:(BOOL) wait {
+ if( pthread_main_np() ) return [self postNotification:notification];
+ [[self class] performSelectorOnMainThread:@selector( _postNotification: ) withObject:notification waitUntilDone:wait];
+}
+
++ (void) _postNotification:(NSNotification *) notification {
+ [[self defaultCenter] postNotification:notification];
+}
+
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object {
+ if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:nil];
+ [self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO];
+}
+
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo {
+ if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:userInfo];
+ [self postNotificationOnMainThreadWithName:name object:object userInfo:userInfo waitUntilDone:NO];
+}
+
+- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo waitUntilDone:(BOOL) wait {
+ if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:userInfo];
+
+ NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] initWithCapacity:3];
+ if( name ) [info setObject:name forKey:@"name"];
+ if( object ) [info setObject:object forKey:@"object"];
+ if( userInfo ) [info setObject:userInfo forKey:@"userInfo"];
+
+ [[self class] performSelectorOnMainThread:@selector( _postNotificationName: ) withObject:info waitUntilDone:wait];
+
+ [info release];
+}
+
++ (void) _postNotificationName:(NSDictionary *) info {
+ NSString *name = [info objectForKey:@"name"];
+ id object = [info objectForKey:@"object"];
+ NSDictionary *userInfo = [info objectForKey:@"userInfo"];
+
+ [[self defaultCenter] postNotificationName:name object:object userInfo:userInfo];
+}
+
+@end