Skip to content

Commit

Permalink
Merge pull request #769 from Sequel-Ace/consistent-encoding-utfmb4
Browse files Browse the repository at this point in the history
Consistently use encoding utf8mb4 #changed
  • Loading branch information
Kaspik committed Jan 12, 2021
2 parents 056bb81 + 98a52fa commit 656cc94
Show file tree
Hide file tree
Showing 24 changed files with 169 additions and 714 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,13 @@ @implementation SPMySQLConnection (Databases_and_Tables_Private_API)
- (BOOL)_storeAndAlterEncodingToUTF8IfRequired
{
// If the encoding is already UTF8, no change is required.
if ([encoding isEqualToString:@"utf8"] && !encodingUsesLatin1Transport) return NO;
if ([encoding hasPrefix:@"utf8"] && !encodingUsesLatin1Transport) {
return NO;
}

// Store the current encoding for restoration afterwards, and update encoding
[self storeEncodingForRestoration];
[self setEncoding:@"utf8"];
[self setEncoding:@"utf8mb4"];

return YES;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,6 @@ - (BOOL)encodingUsesLatin1Transport
*/
- (BOOL)setEncoding:(NSString *)theEncoding
{
// MySQL versions prior to 4.1 don't support encoding changes; return NO on those
// versions.
if (![self serverVersionIsGreaterThanOrEqualTo:4 minorVersion:1 releaseVersion:0]) {
return NO;
}

// If the supplied encoding is already set, return success
if ([encoding isEqualToString:theEncoding] && !encodingUsesLatin1Transport) {
Expand Down Expand Up @@ -120,12 +115,6 @@ - (BOOL)setEncoding:(NSString *)theEncoding
*/
- (BOOL)setEncodingUsesLatin1Transport:(BOOL)useLatin1
{
// MySQL versions prior to 4.1 don't support encoding changes; return NO on those
// versions.
if (![self serverVersionIsGreaterThanOrEqualTo:4 minorVersion:1 releaseVersion:0]) {
return NO;
}

// If the Latin1 mode is already set, return success
if (encodingUsesLatin1Transport == useLatin1) {
return YES;
Expand Down Expand Up @@ -203,7 +192,9 @@ - (void)restoreStoredEncoding
+ (NSStringEncoding)stringEncodingForMySQLCharset:(const char *)mysqlCharset
{
// Handle the most common cases first
if (!strcmp(mysqlCharset, "utf8")) {
if (!strcmp(mysqlCharset, "utf8mb4")) {
return NSUTF8StringEncoding;
} else if (!strcmp(mysqlCharset, "utf8")) {
return NSUTF8StringEncoding;
} else if (!strcmp(mysqlCharset, "latin1")) {
return NSWindowsCP1252StringEncoding; // Warning: This is NOT the same as ISO-8859-1 (aka "ISO Latin 1")
Expand Down Expand Up @@ -255,8 +246,6 @@ + (NSStringEncoding)stringEncodingForMySQLCharset:(const char *)mysqlCharset
return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSLatin2);
} else if (!strcmp(mysqlCharset, "latin7")) {
return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin7);
} else if (!strcmp(mysqlCharset, "utf8mb4")) {
return NSUTF8StringEncoding;
} else if (!strcmp(mysqlCharset, "cp1251")) {
return NSWindowsCP1251StringEncoding;
} else if (!strcmp(mysqlCharset, "utf16")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,8 @@ - (id)queryString:(NSString *)theQueryString usingEncoding:(NSStringEncoding)the
theErrorID = 1317;
theSqlstate = @"70100";

// If the query was cancelled on a MySQL <5 server, check the connection to allow reconnects
// after query kills. This is also handled within the class for internal cancellations, but
// as other external classes may also cancel the query.
if (![self serverVersionIsGreaterThanOrEqualTo:5 minorVersion:0 releaseVersion:0]) {
[self _unlockConnection];
[self checkConnection];
}
[self _unlockConnection];
[self checkConnection];
}

// Unlock the connection if appropriate - if not a streaming result type.
Expand Down Expand Up @@ -560,7 +555,7 @@ - (void)cancelCurrentQuery

// The query cancellation cannot occur on the connection actively running a query
// so set up a new connection to run the KILL command.
MYSQL *killerConnection = [self _makeRawMySQLConnectionWithEncoding:@"utf8" isMasterConnection:NO];
MYSQL *killerConnection = [self _makeRawMySQLConnectionWithEncoding:@"utf8mb4" isMasterConnection:NO];

// If the new connection was successfully set up, use it to run a KILL command.
if (killerConnection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ - (BOOL)killQueryOnThreadID:(unsigned long)theThreadID
{
// Note that mysql_kill has been deprecated, so use a query to perform this task.
NSMutableString *killQuery = [NSMutableString stringWithString:@"KILL"];

//Special suppot for TiDB SQL variant
if ([[self serverVersionString] rangeOfString:@"TiDB"].location != NSNotFound) {
[killQuery appendString:@" TIDB"];
}
else if ([self serverVersionIsGreaterThanOrEqualTo:5 minorVersion:0 releaseVersion:0]) {
[killQuery appendString:@" QUERY"];
}
[killQuery appendFormat:@" %lu", theThreadID];

[killQuery appendFormat:@" QUERY %lu", theThreadID];

// Run the query
[self queryString:killQuery];
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ - (instancetype)init
keepAliveLastPingBlocked = NO;

// Set up default encoding variables
encoding = @"utf8";
encoding = @"utf8mb4";
stringEncoding = NSUTF8StringEncoding;
encodingUsesLatin1Transport = NO;
encodingToRestore = nil;
Expand Down
6 changes: 3 additions & 3 deletions Resources/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,9 @@
/* Create syntaxes for selected items label */
"Create syntaxes for selected items" = "Create syntaxes for selected items";

/* Table Info Section : table create options */
"create_options: %@" = "create_options: %@";

/* created: %@
Table Info Section : time+date table was created at */
"created: %@" = "created: %@";
Expand Down Expand Up @@ -2865,9 +2868,6 @@
/* error removing host informative message */
"This user doesn't seem to have any associated hosts and will be removed unless a host is added." = "This user doesn't seem to have any associated hosts and will be removed unless a host is added.";

/* triggers not supported label */
"This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2" = "This version of MySQL does not support triggers. Support for triggers was added in MySQL 5.0.2";

/* shutdown server : confirmation dialog : message */
"This will wait for open transactions to complete and then quit the mysql daemon. Afterwards neither you nor anyone else can connect to this database!\n\nFull management access to the server's operating system is required to restart MySQL!" = "This will wait for open transactions to complete and then quit the mysql daemon. Afterwards neither you nor anyone else can connect to this database!\n\nFull management access to the server's operating system is required to restart MySQL!";

Expand Down
129 changes: 15 additions & 114 deletions Source/Controllers/DataControllers/SPDatabaseData.m
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,7 @@ - (NSArray *)getDatabaseCollations
if ([collations count] == 0) {

// Try to retrieve the available collations from the database
if ([serverSupport supportsInformationSchema]) {
[collations addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT * FROM `information_schema`.`collations` ORDER BY `collation_name` ASC"]];
}
else if([serverSupport supportsShowCollation]) {
//use the 4.1-style query
NSArray *supportedCollations = [self _getDatabaseDataForQuery:@"SHOW COLLATION"];
//apply the sorting
supportedCollations = [supportedCollations sortedArrayUsingFunction:_sortMySQL4CollationEntry context:nil];
//convert the output to the information_schema style
[collations addObjectsFromArray:[SPDatabaseData _relabelCollationResult:supportedCollations]];
}
[collations addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT * FROM `information_schema`.`collations` ORDER BY `collation_name` ASC"]];

// If that failed, get the list of collations from the hard-coded list
if (![collations count]) {
Expand Down Expand Up @@ -162,17 +152,7 @@ - (NSArray *)getDatabaseCollationsForEncoding:(NSString *)encoding
}

// Try to retrieve the available collations for the supplied encoding from the database
if ([serverSupport supportsInformationSchema]) {
[characterSetCollations addObjectsFromArray:[self _getDatabaseDataForQuery:[NSString stringWithFormat:@"SELECT * FROM `information_schema`.`collations` WHERE character_set_name = '%@' ORDER BY `collation_name` ASC", characterSetEncoding]]];
}
else if([serverSupport supportsShowCollation]) {
//use the 4.1-style query (as every collation name starts with the charset name we can use the prefix search)
NSArray *supportedCollations = [self _getDatabaseDataForQuery:[NSString stringWithFormat:@"SHOW COLLATION LIKE '%@%%'",characterSetEncoding]];
//apply the sorting
supportedCollations = [supportedCollations sortedArrayUsingFunction:_sortMySQL4CollationEntry context:nil];

[characterSetCollations addObjectsFromArray:[SPDatabaseData _relabelCollationResult:supportedCollations]];
}
[characterSetCollations addObjectsFromArray:[self _getDatabaseDataForQuery:[NSString stringWithFormat:@"SELECT * FROM `information_schema`.`collations` WHERE character_set_name = '%@' ORDER BY `collation_name` ASC", characterSetEncoding]]];

// If that failed, get the list of collations matching the supplied encoding from the hard-coded list
if (![characterSetCollations count]) {
Expand Down Expand Up @@ -244,71 +224,15 @@ - (NSString *)getEncodingFromCollation:(NSString *)collation {
- (NSArray *)getDatabaseStorageEngines
{
if ([storageEngines count] == 0) {
if ([serverSupport isMySQL3] || [serverSupport isMySQL4]) {
[storageEngines addObject:@{@"Engine" : @"MyISAM"}];

// Check if InnoDB support is enabled
NSString *result = [self _getSingleVariableValue:@"have_innodb"];

if (result && [result isEqualToString:@"YES"])
{
[storageEngines addObject:@{@"Engine" : @"InnoDB"}];
}

// Before MySQL 4.1 the MEMORY engine was known as HEAP and the ISAM engine was included
if ([serverSupport supportsPre41StorageEngines]) {
[storageEngines addObject:@{@"Engine" : @"HEAP"}];
[storageEngines addObject:@{@"Engine" : @"ISAM"}];
}
else {
[storageEngines addObject:@{@"Engine" : @"MEMORY"}];
}

// BLACKHOLE storage engine was added in MySQL 4.1.11
if ([serverSupport supportsBlackholeStorageEngine]) {
[storageEngines addObject:@{@"Engine" : @"BLACKHOLE"}];
}

// ARCHIVE storage engine was added in MySQL 4.1.3
if ([serverSupport supportsArchiveStorageEngine]) {
[storageEngines addObject:@{@"Engine" : @"ARCHIVE"}];
}

// CSV storage engine was added in MySQL 4.1.4
if ([serverSupport supportsCSVStorageEngine]) {
[storageEngines addObject:@{@"Engine" : @"CSV"}];
}
}
// The table information_schema.engines didn't exist until MySQL 5.1.5
else {
if ([serverSupport supportsInformationSchemaEngines])
{
// Check the information_schema.engines table is accessible
SPMySQLResult *result = [connection queryString:@"SHOW TABLES IN information_schema LIKE 'ENGINES'"];

if ([result numberOfRows] == 1) {

// Table is accessible so get available storage engines
// Note, that the case of the column names specified in this query are important.
[storageEngines addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT Engine, Support FROM `information_schema`.`engines` WHERE SUPPORT IN ('DEFAULT', 'YES') AND Engine != 'PERFORMANCE_SCHEMA'"]];
}
}
else {
// Get storage engines
NSArray *engines = [self _getDatabaseDataForQuery:@"SHOW STORAGE ENGINES"];

// We only want to include engines that are supported
for (NSDictionary *engine in engines)
{
if (([[engine objectForKey:@"Support"] isEqualToString:@"DEFAULT"] ||
[[engine objectForKey:@"Support"] isEqualToString:@"YES"]) &&
![[engine objectForKey:@"Engine"] isEqualToString:@"PERFORMANCE_SCHEMA"])
{
[storageEngines addObject:engine];
}
}
}
}
// Check the information_schema.engines table is accessible
SPMySQLResult *result = [connection queryString:@"SHOW TABLES IN information_schema LIKE 'ENGINES'"];

if ([result numberOfRows] == 1) {

// Table is accessible so get available storage engines
// Note, that the case of the column names specified in this query are important.
[storageEngines addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT Engine, Support FROM `information_schema`.`engines` WHERE SUPPORT IN ('DEFAULT', 'YES') AND Engine != 'PERFORMANCE_SCHEMA'"]];
}
}

return [storageEngines sortedArrayUsingFunction:_sortStorageEngineEntry context:nil];
Expand All @@ -332,26 +256,7 @@ - (NSArray *)getDatabaseCharacterSetEncodings

// Try to retrieve the available character set encodings from the database
// Check the information_schema.character_sets table is accessible
if ([serverSupport supportsInformationSchema]) {
[characterSetEncodings addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT * FROM `information_schema`.`character_sets` ORDER BY `character_set_name` ASC"]];
}
else if ([serverSupport supportsShowCharacterSet]) {
NSArray *supportedEncodings = [self _getDatabaseDataForQuery:@"SHOW CHARACTER SET"];

supportedEncodings = [supportedEncodings sortedArrayUsingFunction:_sortMySQL4CharsetEntry context:nil];

for (NSDictionary *anEncoding in supportedEncodings)
{
NSDictionary *convertedEncoding = [NSDictionary dictionaryWithObjectsAndKeys:
[anEncoding objectForKey:@"Charset"], @"CHARACTER_SET_NAME",
[anEncoding objectForKey:@"Description"], @"DESCRIPTION",
[anEncoding objectForKey:@"Default collation"], @"DEFAULT_COLLATE_NAME",
[anEncoding objectForKey:@"Maxlen"], @"MAXLEN",
nil];

[characterSetEncodings addObject:convertedEncoding];
}
}
[characterSetEncodings addObjectsFromArray:[self _getDatabaseDataForQuery:@"SELECT * FROM `information_schema`.`character_sets` ORDER BY `character_set_name` ASC"]];

// If that failed, get the list of character set encodings from the hard-coded list
if (![characterSetEncodings count]) {
Expand All @@ -374,9 +279,7 @@ - (NSString *)getDatabaseDefaultCharacterSet
{
@synchronized(charsetCollationLock) {
if (!defaultCharacterSetEncoding) {
NSString *variable = [serverSupport supportsCharacterSetAndCollationVars] ? @"character_set_database" : @"character_set";

defaultCharacterSetEncoding = [self _getSingleVariableValue:variable];
defaultCharacterSetEncoding = [self _getSingleVariableValue:@"character_set_database"];
}

return [defaultCharacterSetEncoding copy];
Expand All @@ -393,7 +296,7 @@ - (NSString *)getDatabaseDefaultCharacterSet
- (NSString *)getDatabaseDefaultCollation
{
@synchronized(charsetCollationLock) {
if (!defaultCollation && [serverSupport supportsCharacterSetAndCollationVars]) {
if (!defaultCollation) {
defaultCollation = [self _getSingleVariableValue:@"collation_database"];
}

Expand All @@ -412,9 +315,7 @@ - (NSString *)getServerDefaultCharacterSet
{
@synchronized(charsetCollationLock) {
if (!serverDefaultCharacterSetEncoding) {
NSString *variable = [serverSupport supportsCharacterSetAndCollationVars] ? @"character_set_server" : @"character_set";

serverDefaultCharacterSetEncoding = [self _getSingleVariableValue:variable];
serverDefaultCharacterSetEncoding = [self _getSingleVariableValue:@"character_set_server"];
}

return [serverDefaultCharacterSetEncoding copy];
Expand Down
4 changes: 2 additions & 2 deletions Source/Controllers/DataControllers/SPDatabaseStructure.m
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,8 @@ - (BOOL)_ensureConnectionUnsafe
// Attempt a connection
if (![mySQLConnection connect]) return NO;

// Ensure the encoding is set to UTF8
[mySQLConnection setEncoding:@"utf8"];
// Ensure the encoding is set to UTF8mb4
[mySQLConnection setEncoding:@"utf8mb4"];

// Return success
return YES;
Expand Down

0 comments on commit 656cc94

Please sign in to comment.