aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework/Source
diff options
context:
space:
mode:
authorMax <post@wickenrode.com>2016-03-03 21:37:47 +0100
committerMax <post@wickenrode.com>2016-03-03 21:37:47 +0100
commit43cafb58805e846a0e8da20518de7575cfb23166 (patch)
treea40f24843840766938687a26278e6911559ff402 /Frameworks/SPMySQLFramework/Source
parentba04386f728a8606c6a0c3f4b121cbe74c1d051f (diff)
downloadsequelpro-43cafb58805e846a0e8da20518de7575cfb23166.tar.gz
sequelpro-43cafb58805e846a0e8da20518de7575cfb23166.tar.bz2
sequelpro-43cafb58805e846a0e8da20518de7575cfb23166.zip
Fix #2150 & #2173 on 1.1.x branch
Backport of 2b52f76ed2103bc6d458767906753814ee8ba9e1 & 2b52f76ed2103bc6d458767906753814ee8ba9e1
Diffstat (limited to 'Frameworks/SPMySQLFramework/Source')
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h1
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m20
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult.m29
3 files changed, 45 insertions, 5 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index 199d4790..2419d7a9 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -93,6 +93,7 @@
@interface SPMySQLResult (Private_API)
- (NSString *)_stringWithBytes:(const void *)bytes length:(NSUInteger)length;
+- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy;
- (void)_setQueryExecutionTime:(double)theExecutionTime;
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
index b70d2032..e74f061f 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m
@@ -44,6 +44,7 @@
@interface SPMySQLResult (Private_API)
- (NSString *)_stringWithBytes:(const void *)bytes length:(NSUInteger)length;
+- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy;
@end
@@ -231,24 +232,33 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] =
// Record the original column position within the result set
[eachField setObject:[NSString stringWithFormat:@"%llu", (unsigned long long)i] forKey:@"datacolumnindex"];
+ // mysqlField.name might point to an empty string or NULL (theoretically).
+ // _stringWithBytes:... will return @"" if either bytes is NULL or length is 0.
+ // For now let's interpret (bytes != NULL) as a valid string (possibly empty)
+ // and otherwise as 'value not set'.
+
// Record the column name, or alias if one is being used
- [eachField setObject:[self _stringWithBytes:mysqlField.name length:mysqlField.name_length] forKey:@"name"];
+ if (mysqlField.name) {
+ [eachField setObject:[self _lossyStringWithBytes:mysqlField.name length:mysqlField.name_length wasLossy:NULL] forKey:@"name"];
+ }
// Record the original column name if using an alias
- [eachField setObject:[self _stringWithBytes:mysqlField.org_name length:mysqlField.org_name_length] forKey:@"org_name"];
+ if (mysqlField.org_name) {
+ [eachField setObject:[self _stringWithBytes:mysqlField.org_name length:mysqlField.org_name_length] forKey:@"org_name"];
+ }
// If the column had an underlying table, record the table name, respecting aliases
- if (mysqlField.table_length) {
+ if (mysqlField.table) {
[eachField setObject:[self _stringWithBytes:mysqlField.table length:mysqlField.table_length] forKey:@"table"];
}
// If the column had an underlying table, record the original table name, ignoring aliases
- if (mysqlField.org_table_length) {
+ if (mysqlField.org_table) {
[eachField setObject:[self _stringWithBytes:mysqlField.org_table length:mysqlField.org_table_length] forKey:@"org_table"];
}
// If the column had an underlying database, record the database name
- if (mysqlField.db_length) {
+ if (mysqlField.db) {
[eachField setObject:[self _stringWithBytes:mysqlField.db length:mysqlField.db_length] forKey:@"db"];
}
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
index 56b50d9a..6d2bdfd6 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
@@ -318,6 +318,35 @@ static id NSNullPointer;
return [[[NSString alloc] initWithBytes:bytes length:length encoding:stringEncoding] autorelease];
}
+- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy
+{
+ //mysql protocol limits column names to 256 bytes.
+ //with inline columns and multibyte charsets this can result in a character
+ //being split in half at which the method above will fail.
+ //Let's first try removing stuff from the end to create something valid.
+ NSUInteger removed = 0;
+ do {
+ NSString *res = [self _stringWithBytes:bytes length:(length-removed)];
+ if(res) {
+ if(outLossy) *outLossy = (removed != 0);
+ return (removed? [res stringByAppendingString:@"…"] : res);
+ }
+ removed++;
+ } while(removed <= 10 && removed < length); // 10 is arbitrary
+
+ //if that fails, ascii should accept all values from 0-255 as input
+ NSString *ascii = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding];
+ if(ascii){
+ if(outLossy) *outLossy = YES;
+ return [ascii autorelease];
+ }
+
+ //if even that failed we lose.
+ NSDictionary *info = @{ @"data": [NSData dataWithBytes:bytes length:length] };
+ NSString *reason = [NSString stringWithFormat:@"Failed to convert byte sequence %@ to string (encoding = %lu)",info[@"data"],stringEncoding];
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:info];
+}
+
/**
* Allow setting the execution time for the original query (including connection lag)
* so it can be requested later without relying on connection state.