aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework
diff options
context:
space:
mode:
authorMax <post@wickenrode.com>2015-06-19 21:44:23 +0200
committerMax <post@wickenrode.com>2015-06-19 21:44:23 +0200
commit2b52f76ed2103bc6d458767906753814ee8ba9e1 (patch)
tree6cda5dc4fdabd24ce673b956c5096c06a86a1d3b /Frameworks/SPMySQLFramework
parentce3353422006e927be3005123223c20d8e09b539 (diff)
downloadsequelpro-2b52f76ed2103bc6d458767906753814ee8ba9e1.tar.gz
sequelpro-2b52f76ed2103bc6d458767906753814ee8ba9e1.tar.bz2
sequelpro-2b52f76ed2103bc6d458767906753814ee8ba9e1.zip
Fix an issue (affecting mostly Russian & Asian users) where Sequel Pro would error if the mysql server truncated a column name (fixes #2150)
Diffstat (limited to 'Frameworks/SPMySQLFramework')
-rw-r--r--Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m5
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h1
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult Categories/Field Definitions.m13
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLResult.m32
4 files changed, 46 insertions, 5 deletions
diff --git a/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m
index 6a49daf4..db63e12e 100644
--- a/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m
+++ b/Frameworks/SPMySQLFramework/SPMySQLEmptyResult.m
@@ -126,6 +126,11 @@
return nil;
}
+- (NSString *)_lossyStringWithBytes:(const void *)bytes length:(NSUInteger)length wasLossy:(BOOL *)outLossy
+{
+ return nil;
+}
+
- (id)_getObjectFromBytes:(char *)bytes ofLength:(NSUInteger)length fieldDefinitionIndex:(NSUInteger)fieldIndex previewLength:(NSUInteger)previewLength
{
return nil;
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..d0eac4a1 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
@@ -232,11 +233,15 @@ const SPMySQLResultCharset SPMySQLCharsetMap[] =
[eachField setObject:[NSString stringWithFormat:@"%llu", (unsigned long long)i] forKey:@"datacolumnindex"];
// 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_length) {
+ [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_length) {
+ [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) {
[eachField setObject:[self _stringWithBytes:mysqlField.table length:mysqlField.table_length] forKey:@"table"];
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
index 56b50d9a..421f0435 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLResult.m
@@ -31,6 +31,7 @@
#import "SPMySQLResult.h"
#import "SPMySQL Private APIs.h"
#import "SPMySQLArrayAdditions.h"
+#include <stdlib.h>
static id NSNullPointer;
@@ -98,7 +99,7 @@ static id NSNullPointer;
// Cache the field definitions and build up an array of cached field names and types
fieldDefinitions = mysql_fetch_fields(resultSet);
- fieldNames = malloc(sizeof(NSString *) * numberOfFields);
+ fieldNames = calloc(numberOfFields,sizeof(NSString *));
for (NSUInteger i = 0; i < numberOfFields; i++) {
MYSQL_FIELD aField = fieldDefinitions[i];
fieldNames[i] = [[self _stringWithBytes:aField.name length:aField.name_length] retain];
@@ -318,6 +319,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? [NSString stringWithFormat:@"%@…",res] : 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.