From fac661c5d1dcac7780521facdaebdc6f4c1afa27 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 2 May 2017 01:14:27 +0200 Subject: Fix some issues with charset handling during connect * hostname and file paths are places where libmysqlclient ultimately only passes the string to an OS library so we should use whatever charset the OS expects, not mysql * Even though _makeRawMySQLConnectionWithEncoding:isMasterConnection: takes an explicit charset argument most of the conversion logic simply used whatever charset the existing connection currently has, which is not neccesarily the one the new connection should use * Add some remarks about the charset handling with passwords and mysql_error() * Charsets do not apply to sqlstate. --- .../Source/SPMySQLConnection Categories/Conversion.h | 14 ++++++++++++++ .../Source/SPMySQLConnection Categories/Conversion.m | 14 +++++++++----- .../SPMySQLConnection Categories/Querying & Preparation.m | 9 ++++++--- 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories') diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.h index 6f7b1a9a..77b70bf9 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.h @@ -34,6 +34,7 @@ @interface SPMySQLConnection (Conversion) + (const char *)_cStringForString:(NSString *)aString usingEncoding:(NSStringEncoding)anEncoding returningLengthAs:(NSUInteger *)cStringLengthPointer; ++ (NSString *)_stringForCString:(const char *)cString usingEncoding:(NSStringEncoding)encoding; - (const char *)_cStringForString:(NSString *)aString; - (NSString *)_stringForCString:(const char *)cString; @@ -56,3 +57,16 @@ static inline const char* _cStringForStringWithEncoding(NSString* aString, NSStr return (const char *)(*cachedMethodPointer)(cachedClass, cachedSelector, aString, anEncoding, cStringLengthPointer); } + +/** + * Converts a C string (NUL-terminated) to an NSString using the supplied encoding. + * + * Unlike +[NSString stringWithCString:encoding:] which will crash on a NULL pointer, this method will return nil instead. + */ +static inline NSString * _stringForCStringWithEncoding(const char *aString, NSStringEncoding inputEncoding) +{ + //This implementation is smaller than the cached selector voodoo above, so let's do it inline + + //NSString will crash on NULL ptr + return (aString == NULL)? nil : [NSString stringWithCString:aString encoding:inputEncoding]; +} diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.m index 676684ca..a7d293ea 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Conversion.m @@ -79,7 +79,7 @@ } /** - * Converts a C string to an NSString using the supplied encoding. + * Converts a C string to an NSString using the current connection encoding. * This method *will not* correctly preserve nul characters within c strings; instead * the first nul character within the string will be treated as the line ending. This * is unavoidable without supplying a string length, so this method should not be widely @@ -87,11 +87,15 @@ */ - (NSString *)_stringForCString:(const char *)cString { + return _stringForCStringWithEncoding(cString, stringEncoding); +} - // Don't try and convert null strings - if (cString == NULL) return nil; - - return [NSString stringWithCString:cString encoding:stringEncoding]; +/** + * @see _stringForCStringWithEncoding() + */ ++ (NSString *)_stringForCString:(const char *)cString usingEncoding:(NSStringEncoding)encoding +{ + return _stringForCStringWithEncoding(cString, encoding); } @end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m index 594756be..ef98a21c 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m @@ -323,7 +323,8 @@ // Store the error state theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; theErrorID = mysql_errno(mySQLConnection); - theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; + // sqlstate is always an ASCII string, regardless of charset (but use latin1 anyway as that is less picky about invalid bytes) + theSqlstate = _stringForCStringWithEncoding(mysql_sqlstate(mySQLConnection), NSISOLatin1StringEncoding); // Prevent retries if the query was cancelled or not a connection error if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:theErrorID]) { @@ -382,7 +383,8 @@ // Update the error message, if appropriate, to reflect result store errors or overall success theErrorMessage = [self _stringForCString:mysql_error(mySQLConnection)]; theErrorID = mysql_errno(mySQLConnection); - theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; + // sqlstate is always an ASCII string, regardless of charset (but use latin1 anyway as that is less picky about invalid bytes) + theSqlstate = _stringForCStringWithEncoding(mysql_sqlstate(mySQLConnection), NSISOLatin1StringEncoding); } else { theResult = [[SPMySQLEmptyResult alloc] init]; } @@ -735,7 +737,8 @@ { // If a SQLSTATE wasn't supplied, select one from the connection if(!theSqlstate) { - theSqlstate = [self _stringForCString:mysql_sqlstate(mySQLConnection)]; + // sqlstate is always an ASCII string, regardless of charset (but use latin1 anyway as that is less picky about invalid bytes) + theSqlstate = _stringForCStringWithEncoding(mysql_sqlstate(mySQLConnection), NSISOLatin1StringEncoding); } // Clear the last SQLSTATE stored on the instance -- cgit v1.2.3