Permalink
Browse files

* Lock connection during disconnect to prevent some race conditions

* Always use the server version name provided by mysql_get_server_info() as that should me more reliable
* Use mysql_get_server_version() for version comparisons. Less code, official API and closer to what mysql does
  • Loading branch information...
dmoagx committed Oct 30, 2015
1 parent 2b2a177 commit b2d798ba9282d3acf1a2d65de30849e529d4d255
@@ -94,15 +94,15 @@ - (void)_threadedKeepAlive
// attempt a single reconnection in the background
if (_elapsedSecondsSinceAbsoluteTime(lastConnectionUsedTime) < 60 * 15) {
[self _reconnectAfterBackgroundConnectionLoss];
}
// Otherwise set the state to connection lost for automatic reconnect on
// next use.
} else {
else {
state = SPMySQLConnectionLostInBackground;
}
// Return as no further ping action required this cycle.
goto end;
goto end_cleanup;
}
// Otherwise, perform a background ping.
@@ -112,7 +112,7 @@ - (void)_threadedKeepAlive
} else {
keepAlivePingFailures++;
}
end:
end_cleanup:
keepAliveThread = nil;
}
@@ -139,6 +139,11 @@ - (BOOL)_pingConnectionUsingLoopDelay:(NSUInteger)loopDelay
// Set up a query lock
[self _lockConnection];
//we might find ourselves at the losing end of a contest with -[self _disconnect]
if(!mySQLConnection) {
[self _unlockConnection];
return NO;
}
volatile BOOL keepAliveLastPingSuccess = NO;
keepAliveLastPingBlocked = NO;
@@ -46,54 +46,34 @@ - (NSString *)serverVersionString
return [NSString stringWithString:serverVariableVersion];
}
#warning FIXME: There is probably a race condition here with -[self _disconnect]
if(mySQLConnection) {
return [self _stringForCString:mysql_get_server_info(mySQLConnection)];
}
return nil;
}
/**
* Return the server major version or NSNotFound on failure
* Return the server major version or 0 on failure
*/
- (NSUInteger)serverMajorVersion
{
NSString *ver;
if ((ver = [self serverVersionString]) != nil) {
NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:0];
return (NSUInteger)[s integerValue];
}
return NSNotFound;
// 5.5.33 => 50533 / 10'000 => 5.0533 => 5
return (serverVersionNumber / 10000);
}
/**
* Return the server minor version or NSNotFound on failure
* Return the server minor version or 0 on failure
*/
- (NSUInteger)serverMinorVersion
{
NSString *ver;
if ((ver = [self serverVersionString]) != nil) {
NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:1];
return (NSUInteger)[s integerValue];
}
return NSNotFound;
// 5.5.33 => 50533 - (5*10'000) => 533 / 100 => 5.33 => 5
return ((serverVersionNumber - [self serverMajorVersion]*10000) / 100);
}
/**
* Return the server release version or NSNotFound on failure
* Return the server release version or 0 on failure
*/
- (NSUInteger)serverReleaseVersion
{
NSString *ver;
if ((ver = [self serverVersionString]) != nil) {
NSString *s = [[ver componentsSeparatedByString:@"."] objectAtIndex:2];
return (NSUInteger)[[[s componentsSeparatedByString:@"-"] objectAtIndex:0] integerValue];
}
return NSNotFound;
// 5.5.33 => 50533 - (5*10'000 + 5*100) => 33
return (serverVersionNumber - ([self serverMajorVersion]*10000 + [self serverMinorVersion]*100));
}
#pragma mark -
@@ -105,23 +85,9 @@ - (NSUInteger)serverReleaseVersion
*/
- (BOOL)serverVersionIsGreaterThanOrEqualTo:(NSUInteger)aMajorVersion minorVersion:(NSUInteger)aMinorVersion releaseVersion:(NSUInteger)aReleaseVersion
{
NSString *ver;
if (!(ver = [self serverVersionString])) return NO;
NSArray *serverVersionParts = [ver componentsSeparatedByString:@"."];
NSUInteger serverMajorVersion = (NSUInteger)[[serverVersionParts objectAtIndex:0] integerValue];
if (serverMajorVersion < aMajorVersion) return NO;
if (serverMajorVersion > aMajorVersion) return YES;
NSUInteger serverMinorVersion = (NSUInteger)[[serverVersionParts objectAtIndex:1] integerValue];
if (serverMinorVersion < aMinorVersion) return NO;
if (serverMinorVersion > aMinorVersion) return YES;
unsigned long myver = aMajorVersion * 10000 + aMinorVersion * 100 + aReleaseVersion;
NSString *serverReleasePart = [serverVersionParts objectAtIndex:2];
NSUInteger serverReleaseVersion = (NSUInteger)[[[serverReleasePart componentsSeparatedByString:@"-"] objectAtIndex:0] integerValue];
if (serverReleaseVersion < aReleaseVersion) return NO;
return YES;
return (myver >= serverVersionNumber);
}
#pragma mark -
@@ -99,6 +99,7 @@
// Server details
NSString *serverVariableVersion;
unsigned long serverVersionNumber;
// Error state for the last query or connection state
NSUInteger queryErrorID;
@@ -174,6 +174,7 @@ - (id)init
// Ensure the server detail records are initialised
serverVariableVersion = nil;
serverVersionNumber = 0;
// Start with a blank error state
queryErrorID = 0;
@@ -475,6 +476,19 @@ - (BOOL)_connect
mysqlConnectionThreadId = mySQLConnection->thread_id;
lastConnectionUsedTime = initialConnectTime;
// Copy the server version string to the instance variable
if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
// the mysql_get_server_info() function
// * returns the version name that is part of the initial connection handshake.
// * Unless the connection failed, it will always return a non-null buffer containing at least a '\0'.
// * It will never affect the error variables (since it only returns a struct member)
//
// At that point (handshake) there is no charset and it's highly unlikely this will ever contain something other than ASCII,
// but to be safe, we'll use the Latin1 encoding which won't bail on invalid chars.
serverVariableVersion = [[NSString alloc] initWithCString:mysql_get_server_info(mySQLConnection) encoding:NSISOLatin1StringEncoding];
// this one can actually change the error state, but only if the server version string is not set (ie. no connection)
serverVersionNumber = mysql_get_server_version(mySQLConnection);
// Update SSL state
connectedWithSSL = NO;
if (useSSL) connectedWithSSL = (mysql_get_ssl_cipher(mySQLConnection))?YES:NO;
@@ -892,24 +906,24 @@ - (void)_disconnect
[self _unlockConnection];
[self _cancelKeepAlives];
[self _lockConnection];
// Close the underlying MySQL connection if it still appears to be active, and not reading
// or writing. While this may result in a leak of the MySQL object, it prevents crashes
// due to attempts to close a blocked/stuck connection.
if (mySQLConnection && !mySQLConnection->net.reading_or_writing && mySQLConnection->net.vio && mySQLConnection->net.buff) {
mysql_close(mySQLConnection);
}
mySQLConnection = NULL;
if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
serverVersionNumber = 0;
if (database) [database release], database = nil;
state = SPMySQLDisconnected;
[self _unlockConnection];
// If using a connection proxy, disconnect that too
if (proxy) {
[proxy performSelectorOnMainThread:@selector(disconnect) withObject:nil waitUntilDone:YES];
}
// Clear host-specific information
if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
if (database) [database release], database = nil;
state = SPMySQLDisconnected;
}
/**
@@ -934,10 +948,6 @@ - (void)_updateConnectionVariables
[variables setObject:[variableRow objectAtIndex:1] forKey:[variableRow objectAtIndex:0]];
}
// Copy the server version string to the instance variable
if (serverVariableVersion) [serverVariableVersion release], serverVariableVersion = nil;
serverVariableVersion = [[variables objectForKey:@"version"] retain];
// Get the connection encoding. Although a specific encoding may have been requested on
// connection, it may be overridden by init_connect commands or connection state changes.
// Default to latin1 for older server versions.
@@ -1043,9 +1043,7 @@ - (IBAction)newTableInfo:(id)sender
NSArray *encodings = [databaseDataInstance getDatabaseCharacterSetEncodings];
NSString *utf8MenuItemTitle = nil;
if ([encodings count] > 0
&& ([mySQLConnection serverMajorVersion] > 4
|| ([mySQLConnection serverMajorVersion] == 4 && [mySQLConnection serverMinorVersion] >= 1)))
if ([encodings count] > 0 && ([mySQLConnection serverVersionIsGreaterThanOrEqualTo:4 minorVersion:1 releaseVersion:0]))
{
[[newTableInfoEncodingPopup menu] addItem:[NSMenuItem separatorItem]];
for (NSDictionary *encoding in encodings) {
@@ -95,10 +95,9 @@ @implementation SPServerSupport
- (id)initWithMajorVersion:(NSInteger)majorVersion minor:(NSInteger)minorVersion release:(NSInteger)releaseVersion
{
if ((self = [super init])) {
// Might be NSNotFound if unknown. unknown should also lose against "0".
serverMajorVersion = (majorVersion != NSNotFound)? majorVersion : -1;
serverMinorVersion = (minorVersion != NSNotFound)? minorVersion : -1;
serverReleaseVersion = (releaseVersion != NSNotFound)? releaseVersion : -1;
serverMajorVersion = majorVersion;
serverMinorVersion = minorVersion;
serverReleaseVersion = releaseVersion;
// Determine what the server supports
[self evaluate];
@@ -363,9 +362,9 @@ - (NSComparisonResult)_compareServerMajorVersion:(NSInteger)majorVersionA
- (void)dealloc
{
// Reset version integers
serverMajorVersion = -1;
serverMinorVersion = -1;
serverReleaseVersion = -1;
serverMajorVersion = 0;
serverMinorVersion = 0;
serverReleaseVersion = 0;
// Invalidate all ivars
[self _invalidate];

0 comments on commit b2d798b

Please sign in to comment.