// // $Id$ // // SPServerSupport.m // sequel-pro // // Created by Stuart Connolly (stuconnolly.com) on September 23, 2010 // Copyright (c) 2010 Stuart Connolly. All rights reserved. // // 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 "SPServerSupport.h" #import <objc/runtime.h> @interface SPServerSupport (PrivateAPI) - (void)_invalidate; - (NSComparisonResult)_compareServerMajorVersion:(NSInteger)majorVersionA minor:(NSInteger)minorVersionA release:(NSInteger)releaseVersionA withServerMajorVersion:(NSInteger)majorVersionB minor:(NSInteger)minorVersionB release:(NSInteger)releaseVersionB; @end @implementation SPServerSupport @synthesize isMySQL3; @synthesize isMySQL4; @synthesize isMySQL5; @synthesize isMySQL6; @synthesize supportsInformationSchema; @synthesize supportsSpatialExtensions; @synthesize supportsShowCharacterSet; @synthesize supportsCharacterSetDatabaseVar; @synthesize supportsPost41CharacterSetHandling; @synthesize supportsCreateUser; @synthesize supportsRenameUser; @synthesize supportsDropUser; @synthesize supportsFullDropUser; @synthesize supportsUserMaxVars; @synthesize supportsShowPrivileges; @synthesize engineTypeQueryName; @synthesize supportsInformationSchemaEngines; @synthesize supportsPre41StorageEngines; @synthesize supportsBlackholeStorageEngine; @synthesize supportsArchiveStorageEngine; @synthesize supportsCSVStorageEngine; @synthesize supportsTriggers; @synthesize supportsIndexKeyBlockSize; @synthesize supportsQuotingEngineTypeInCreateSyntax; @synthesize serverMajorVersion; @synthesize serverMinorVersion; @synthesize serverReleaseVersion; #pragma mark - #pragma mark Initialization /** * Creates and returns an instance of SPServerSupport with the supplied version numbers. The caller is * responsible it's memory. * * @param majorVersion The major version number of the server * @param minorVersion The minor version number of the server * @param releaseVersiod The release version number of the server * * @return The initializes SPServerSupport instance */ - (id)initWithMajorVersion:(NSInteger)majorVersion minor:(NSInteger)minorVersion release:(NSInteger)releaseVersion { if ((self = [super init])) { serverMajorVersion = majorVersion; serverMinorVersion = minorVersion; serverReleaseVersion = releaseVersion; // Determine what the server supports [self evaluate]; } return self; } #pragma mark - #pragma mark Public API /** * Performs the actual version based comparisons to determine what functionaity the server supports. This * method is called automatically as part of the designated initializer (initWithMajorVersion:major:minor:release:) * and shouldn't really need to be called again throughout a connection's lifetime. * * Note that for the sake of simplicity this method does not try to be smart in that it does not assume * the presence of functionality based on a previous version check. This allows adding new ivars in the * future a matter of simply performing a new version comparison. * * To add a new metod for determining a server's support for specific functionality, simply add a new * (read only) ivar with the prefix 'supports' and peform the version checking within this method. */ - (void)evaluate { // By default, assumme the server doesn't support anything [self _invalidate]; isMySQL3 = (serverMajorVersion == 3); isMySQL4 = (serverMajorVersion == 4); isMySQL5 = (serverMajorVersion == 5); isMySQL6 = (serverMajorVersion == 6); // The information schema database wasn't added until MySQL 5 supportsInformationSchema = (serverMajorVersion >= 5); // Support for spatial extensions wasn't added until MySQL 4.1 supportsSpatialExtensions = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]; // The SHOW CHARACTER SET statement wasn't added until MySQL 4.1.0 supportsShowCharacterSet = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]; // The variable 'character_set_database' wasn't added until MySQL 4.1.1 supportsCharacterSetDatabaseVar = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:1]; // As of MySQL 4.1 encoding support was greatly improved supportsPost41CharacterSetHandling = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]; // The table information_schema.engines wasn't added until MySQL 5.1.5 supportsInformationSchemaEngines = [self isEqualToOrGreaterThanMajorVersion:5 minor:1 release:1]; // The CREATE USER statement wasn't added until MySQL 5.0.2 supportsCreateUser = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:2]; // The RENAME USER statement wasn't added until MySQL 5.0.2 supportsRenameUser = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:2]; // The DROP USER statement wasn't added until MySQL 4.1.1 supportsDropUser = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:1]; // Similarly before MySQL 5.0.2 the DROP USER statement only removed users with no privileges supportsFullDropUser = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:2]; // The maximum user variable columns (within mysql.user) weren't added until MySQL 4.0.2 supportsUserMaxVars = [self isEqualToOrGreaterThanMajorVersion:4 minor:0 release:2]; // The SHOW PRIVILEGES statement wasn't added until MySQL 4.1.0 supportsShowPrivileges = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]; // MySQL 4.0.18+ and 4.1.2+ changed the TYPE option to ENGINE, but 4.x supports both engineTypeQueryName = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:0] ? @"ENGINE" : @"TYPE"; // Before MySQL 4.1 the MEMORY engine was known as HEAP and the ISAM engine was available supportsPre41StorageEngines = (![self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]); // The BLACKHOLE storage engine wasn't added until MySQL 4.1.11 supportsBlackholeStorageEngine = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:11]; // The ARCHIVE storage engine wasn't added until MySQL 4.1.3 supportsArchiveStorageEngine = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:3]; // The CSV storage engine wasn't added until MySQL 4.1.4 supportsCSVStorageEngine = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:4]; // Support for triggers wasn't added until MySQL 5.0.2 supportsTriggers = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:2]; // Support for specifying an index's key block size wasn't added until MySQL 5.1.10 supportsIndexKeyBlockSize = [self isEqualToOrGreaterThanMajorVersion:5 minor:1 release:10]; // MySQL 4.0 doesn't seem to like having the ENGINE/TYPE quoted in a table's create syntax supportsQuotingEngineTypeInCreateSyntax = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]; } /** * Convenience method provided as an easy way to determine whether the currently connected server version * is equal to or greater than the supplied version numbers. * * This method should only be used in the case that the build in support ivars don't cover the version/functionality * checking that is required. * * @param majorVersion The major version number of the server * @param minorVersion The minor version number of the server * @param releaseVersiod The release version number of the server * * @return A BOOL indicating the result of the comparison. */ - (BOOL)isEqualToOrGreaterThanMajorVersion:(NSInteger)majorVersion minor:(NSInteger)minorVersion release:(NSInteger)releaseVersion; { return ([self _compareServerMajorVersion:serverMajorVersion minor:serverMinorVersion release:serverReleaseVersion withServerMajorVersion:majorVersion minor:minorVersion release:releaseVersion] > NSOrderedAscending); } /** * Provides a general description of this object instance. Note that this should only be used for debugging purposes. * * @return The string describing the object instance */ - (NSString *)description { unsigned int i; NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: Server is MySQL version %d.%d.%d. Supports:\n", [self className], serverMajorVersion, serverMinorVersion, serverReleaseVersion]; Ivar *vars = class_copyIvarList([self class], &i); for (NSUInteger j = 0; j < i; j++) { NSString *varName = [NSString stringWithUTF8String:ivar_getName(vars[j])]; if ([varName hasPrefix:@"supports"]) { [description appendFormat:@"\t%@ = %@\n", varName, (object_getIvar(self, vars[j])) ? @"YES" : @"NO"]; } } [description appendString:@">"]; free(vars); return description; } #pragma mark - #pragma mark Private API /** * Invalidates all knowledge of what we know the server supports by simply reseting all ivars to their * original state, that is, it doesn't support anything. */ - (void)_invalidate { isMySQL3 = NO; isMySQL4 = NO; isMySQL5 = NO; isMySQL6 = NO; supportsInformationSchema = NO; supportsSpatialExtensions = NO; supportsShowCharacterSet = NO; supportsCharacterSetDatabaseVar = NO; supportsPost41CharacterSetHandling = NO; supportsCreateUser = NO; supportsRenameUser = NO; supportsDropUser = NO; supportsFullDropUser = NO; supportsUserMaxVars = NO; supportsShowPrivileges = NO; engineTypeQueryName = @"ENGINE"; supportsInformationSchemaEngines = NO; supportsPre41StorageEngines = NO; supportsBlackholeStorageEngine = NO; supportsArchiveStorageEngine = NO; supportsCSVStorageEngine = NO; supportsTriggers = NO; supportsIndexKeyBlockSize = NO; supportsQuotingEngineTypeInCreateSyntax = NO; } /** * Compares the supplied version numbers to determine their order. * * Note that this method assumes (when comparing MySQL version numbers) that release verions in the form * XX are larger than X. For example, version 5.0.18 is greater than version 5.0.8 * * @param majorVersionA The major version number of server A * @param minorVersionA The minor version number of server A * @param releaseVersionA The release version number of server A * @param majorVersionB The major version number of server B * @param minorVersionB The minor version number of server B * @param releaseVersionB The release version number of server B * * @return One of NSComparisonResult constants indicating the order of the comparison */ - (NSComparisonResult)_compareServerMajorVersion:(NSInteger)majorVersionA minor:(NSInteger)minorVersionA release:(NSInteger)releaseVersionA withServerMajorVersion:(NSInteger)majorVersionB minor:(NSInteger)minorVersionB release:(NSInteger)releaseVersionB { if (majorVersionA > majorVersionB) return NSOrderedDescending; if (majorVersionA < majorVersionB) return NSOrderedAscending; // The major versions are the same so move to checking the minor versions if (minorVersionA > minorVersionB) return NSOrderedDescending; if (minorVersionA < minorVersionB) return NSOrderedAscending; // The minor versions are the same so move to checking the release versions if (releaseVersionA > releaseVersionB) return NSOrderedDescending; if (releaseVersionA < releaseVersionB) return NSOrderedAscending; // Both version numbers are the same return NSOrderedSame; } #pragma mark - #pragma mark Other /** * Dealloc. Invalidate all ivars. */ - (void)dealloc { // Reset version integers serverMajorVersion = -1; serverMinorVersion = -1; serverReleaseVersion = -1; // Invalidate all ivars [self _invalidate]; [super dealloc]; } @end