Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Change the internal handling of charset/collation from offset-based t…
…o name-based in structure view

This is a major change to the charset/collation code. Please watch out for issues!
(part of #2237)
  • Loading branch information
dmoagx committed Nov 1, 2015
1 parent 7e74bcb commit f02fb78
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 193 deletions.
36 changes: 16 additions & 20 deletions Source/SPTableStructure.m
Expand Up @@ -46,6 +46,7 @@
#import "SPTableStructureLoading.h"
#import "SPThreadAdditions.h"
#import "SPServerSupport.h"
#import "SPExtendedTableInfo.h"

#import <SPMySQL/SPMySQL.h>

Expand Down Expand Up @@ -252,8 +253,8 @@ - (IBAction)addField:(id)sender
BOOL allowNull = [[[tableDataInstance statusValueForKey:@"Engine"] uppercaseString] isEqualToString:@"CSV"] ? NO : [prefs boolForKey:SPNewFieldsAllowNulls];

[tableFields insertObject:[NSMutableDictionary
dictionaryWithObjects:[NSArray arrayWithObjects:@"", @"INT", @"", @"0", @"0", @"0", allowNull ? @"1" : @"0", @"", [prefs stringForKey:SPNullValue], @"None", @"", @0, @0, nil]
forKeys:@[@"name", @"type", @"length", @"unsigned", @"zerofill", @"binary", @"null", @"Key", @"default", @"Extra", @"comment", @"encoding", @"collation"]]
dictionaryWithObjects:[NSArray arrayWithObjects:@"", @"INT", @"", @"0", @"0", @"0", allowNull ? @"1" : @"0", @"", [prefs stringForKey:SPNullValue], @"None", @"", nil]
forKeys:@[@"name", @"type", @"length", @"unsigned", @"zerofill", @"binary", @"null", @"Key", @"default", @"Extra", @"comment"]]
atIndex:insertIndex];
#else
[tableFields insertObject:[NSMutableDictionary
Expand Down Expand Up @@ -810,30 +811,25 @@ - (BOOL)addRowToDB


if ([fieldValidation isFieldTypeString:theRowType]) {
BOOL charsetSupport = [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling];

// Add CHARSET
NSString *fieldEncoding = @"";
if([[theRow objectForKey:@"encoding"] integerValue] > 0 && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling]) {
NSString *enc = [[encodingPopupCell itemAtIndex:[[theRow objectForKey:@"encoding"] integerValue]] title];
NSInteger start = [enc rangeOfString:@"("].location+1;
NSInteger end = [enc length] - start - 1;
fieldEncoding = [enc substringWithRange:NSMakeRange(start, end)];
NSString *fieldEncoding = [theRow objectForKey:@"encodingName"];
if(charsetSupport && [fieldEncoding length]) {
[queryString appendFormat:@"\n CHARACTER SET %@", fieldEncoding];
}
// Remember CHARSET for COLLATE
if(![fieldEncoding length] && [tableDataInstance tableEncoding]) {
fieldEncoding = [tableDataInstance tableEncoding];
}

// ADD COLLATE
if([fieldEncoding length] && [[theRow objectForKey:@"collation"] integerValue] > 0 && ![[theRow objectForKey:@"binary"] integerValue]) {
NSArray *theCollations = [databaseDataInstance getDatabaseCollationsForEncoding:fieldEncoding];
NSString *col = [[theCollations objectAtIndex:[[theRow objectForKey:@"collation"] integerValue]-1] objectForKey:@"COLLATION_NAME"];
[queryString appendFormat:@"\n COLLATE %@", col];
}

if ( [[theRow objectForKey:@"binary"] integerValue] == 1) {
if ([[theRow objectForKey:@"binary"] integerValue] == 1) {
[queryString appendString:@"\n BINARY"];
}
else {
// ADD COLLATE
// Note: a collate without charset is valid in MySQL. The charset can be determined from a collation.
NSString *fieldCollation = [theRow objectForKey:@"collationName"];
if(charsetSupport && [fieldCollation length]) {
[queryString appendFormat:@"\n COLLATE %@", fieldCollation];
}
}

}
else if ([fieldValidation isFieldTypeNumeric:theRowType] && (![theRowType isEqualToString:@"BIT"])) {
Expand Down
164 changes: 84 additions & 80 deletions Source/SPTableStructureDelegate.m
Expand Up @@ -37,6 +37,7 @@
#import "SPTableFieldValidation.h"
#import "SPTableStructureLoading.h"
#import "SPServerSupport.h"
#import "SPTablesList.h"

#import <SPMySQL/SPMySQL.h>

Expand All @@ -60,72 +61,68 @@ - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColum
{
// Return a placeholder if the table is reloading
if ((NSUInteger)rowIndex >= [tableFields count]) return @"...";

NSDictionary *rowData = NSArrayObjectAtIndex(tableFields, rowIndex);

if ([[tableColumn identifier] isEqualToString:@"collation"]) {
NSString *tableEncoding = [tableDataInstance tableEncoding];
NSString *columnEncoding = nil;
NSInteger idx = 0;

if ((idx = [[NSArrayObjectAtIndex(tableFields, rowIndex) objectForKey:@"encoding"] integerValue]) > 0 && idx < [encodingPopupCell numberOfItems]) {
NSString *enc = [[encodingPopupCell itemAtIndex:idx] title];
NSString *columnEncoding = [rowData objectForKey:@"encodingName"];
NSString *columnCollation = [rowData objectForKey:@"collationName"]; // loadTable: has already inferred it, if not set explicit

NSUInteger start = [enc rangeOfString:@"("].location + 1;

columnEncoding = [enc substringWithRange:NSMakeRange(start, [enc length] - start - 1)];
collations = [databaseDataInstance getDatabaseCollationsForEncoding:columnEncoding];
}
else {
// If the structure has loaded (not still loading!) and the table encoding
// is set, use the appropriate collations.
collations = @[];
if([tableDocumentInstance structureLoaded]) {
columnEncoding = [tableDataInstance tableEncoding];
if(columnEncoding) collations = [databaseDataInstance getDatabaseCollationsForEncoding:columnEncoding];
}
}

NSPopUpButtonCell *collationCell = [tableColumn dataCell];

[collationCell removeAllItems];

if ([collations count] > 0) {
NSString *tableCollation = [[tableDataInstance statusValues] objectForKey:@"Collation"];

if (![tableCollation length]) {
tableCollation = [databaseDataInstance getDefaultCollationForEncoding:tableEncoding];
}

NSString *columnCollation = [NSArrayObjectAtIndex(tableFields, rowIndex) objectForKey:@"collationName"];

if (![columnCollation length]) {
columnCollation = [databaseDataInstance getDefaultCollationForEncoding:columnEncoding];
}

[[tableColumn dataCell] addItemWithTitle:@""];
[collationCell addItemWithTitle:@"dummy"];
//copy the default style of menu items and add gray color for default item
NSMutableDictionary *menuAttrs = [NSMutableDictionary dictionaryWithDictionary:[[collationCell attributedTitle] attributesAtIndex:0 effectiveRange:NULL]];
[menuAttrs setObject:[NSColor lightGrayColor] forKey:NSForegroundColorAttributeName];
[[collationCell lastItem] setTitle:@""];

//if this is not set the column either has no encoding (numeric etc.) or retrieval failed. Either way we can't provide collations
if(columnEncoding) {
collations = [databaseDataInstance getDatabaseCollationsForEncoding:columnEncoding];

BOOL useMonospacedFont = [prefs boolForKey:SPUseMonospacedFonts];
CGFloat monospacedFontSize = [prefs floatForKey:SPMonospacedFontSize] > 0 ? [prefs floatForKey:SPMonospacedFontSize] : [NSFont smallSystemFontSize];
NSMutableDictionary *menuAttributes = [NSMutableDictionary dictionaryWithObject:[NSColor lightGrayColor] forKey:NSForegroundColorAttributeName];
[menuAttributes setObject:useMonospacedFont ? [NSFont fontWithName:SPDefaultMonospacedFontName size:monospacedFontSize] : [NSFont systemFontOfSize:[NSFont smallSystemFontSize]] forKey:NSFontAttributeName];

BOOL columnUsesTableDefaultEncoding = ([columnEncoding isEqualToString:tableEncoding]);
// Populate collation popup button
for (NSDictionary *collation in collations)
{
NSString *collationName = [collation objectForKey:@"COLLATION_NAME"];

[[tableColumn dataCell] addItemWithTitle:collationName];
if ([collations count] > 0) {
NSString *tableCollation = [[tableDataInstance statusValues] objectForKey:@"Collation"];

// If this matches the table's collation, draw in gray
if (columnUsesTableDefaultEncoding && [collationName isEqualToString:tableCollation]) {
NSMenuItem *collationMenuItem = [(NSPopUpButtonCell *)[tableColumn dataCell] itemAtIndex:([[tableColumn dataCell] numberOfItems] - 1)];
if (![tableCollation length]) {
tableCollation = [databaseDataInstance getDefaultCollationForEncoding:tableEncoding];
}

NSAttributedString *itemString = [[[NSAttributedString alloc] initWithString:collationName attributes:menuAttributes] autorelease];

[collationMenuItem setAttributedTitle:itemString];
BOOL columnUsesTableDefaultEncoding = ([columnEncoding isEqualToString:tableEncoding]);
// Populate collation popup button
for (NSDictionary *collation in collations)
{
NSString *collationName = [collation objectForKey:@"COLLATION_NAME"];

[collationCell addItemWithTitle:collationName];
NSMenuItem *item = [collationCell lastItem];
[item setRepresentedObject:collationName];

// If this matches the table's collation, draw in gray
if (columnUsesTableDefaultEncoding && [collationName isEqualToString:tableCollation]) {
NSAttributedString *itemString = [[NSAttributedString alloc] initWithString:[item title] attributes:menuAttrs];
[item setAttributedTitle:[itemString autorelease]];
}
}

//look up the right item
NSInteger idx = [collationCell indexOfItemWithRepresentedObject:columnCollation];
if(idx > 0) return @(idx);
}
}

return @0;
}
else if ([[tableColumn identifier] isEqualToString:@"encoding"]) {
// the encoding menu was already configured during setTableDetails:
NSString *columnEncoding = [rowData objectForKey:@"encodingName"];

if(columnEncoding) {
NSInteger idx = [encodingPopupCell indexOfItemWithRepresentedObject:columnEncoding];
if(idx > 0) return @(idx);
}

return @0;
}
else if ([[tableColumn identifier] isEqualToString:@"Extra"]) {
id dataCell = [tableColumn dataCell];
Expand All @@ -141,7 +138,7 @@ - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColum
}
}

return [NSArrayObjectAtIndex(tableFields, rowIndex) objectForKey:[tableColumn identifier]];
return [rowData objectForKey:[tableColumn identifier]];
}

- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
Expand All @@ -159,16 +156,32 @@ - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTable

// Reset collation if encoding was changed
if ([[aTableColumn identifier] isEqualToString:@"encoding"]) {
if ([[currentRow objectForKey:@"encoding"] integerValue] != [anObject integerValue]) {
[currentRow setObject:@0 forKey:@"collation"];
NSString *oldEncoding = [currentRow objectForKey:@"encodingName"];
NSString *newEncoding = [[encodingPopupCell itemAtIndex:[anObject integerValue]] representedObject];
if (![oldEncoding isEqualToString:newEncoding]) {
[currentRow removeObjectForKey:@"collationName"];
[tableSourceView reloadData];
}
if(!newEncoding)
[currentRow removeObjectForKey:@"encodingName"];
else
[currentRow setObject:newEncoding forKey:@"encodingName"];
return;
}
else if ([[aTableColumn identifier] isEqualToString:@"collation"]) {
NSString *newCollation = [[(NSPopUpButtonCell *)[aTableColumn dataCell] itemAtIndex:[anObject integerValue]] representedObject];

if(!newCollation)
[currentRow removeObjectForKey:@"collationName"];
else
[currentRow setObject:newCollation forKey:@"collationName"];
return;
}
// Reset collation if BINARY was set changed, as enabling BINARY sets collation to *_bin
else if ([[aTableColumn identifier] isEqualToString:@"binary"]) {
if ([[currentRow objectForKey:@"binary"] integerValue] != [anObject integerValue]) {
[currentRow setObject:@0 forKey:@"collation"];
[currentRow removeObjectForKey:@"collationName"];

[tableSourceView reloadData];
}
}
Expand Down Expand Up @@ -220,9 +233,8 @@ - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTable
[tableSourceView reloadData];
}
}

// Store new value but not if user choose "---" for type and reset values if required
if ([[aTableColumn identifier] isEqualToString:@"type"]) {
else if ([[aTableColumn identifier] isEqualToString:@"type"]) {
if (anObject && [(NSString*)anObject length] && ![(NSString*)anObject hasPrefix:@"--"]) {
[currentRow setObject:[(NSString*)anObject uppercaseString] forKey:@"type"];

Expand All @@ -238,10 +250,10 @@ - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTable

[tableSourceView reloadData];
}
return;
}
else {
[currentRow setObject:(anObject) ? anObject : @"" forKey:[aTableColumn identifier]];
}

[currentRow setObject:(anObject) ? anObject : @"" forKey:[aTableColumn identifier]];
}

/**
Expand Down Expand Up @@ -270,7 +282,7 @@ - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)r

if ([rows count] == 1) {
[pboard declareTypes:@[SPDefaultPasteboardDragType] owner:nil];
[pboard setString:[[NSNumber numberWithInteger:[rows firstIndex]] stringValue] forType:SPDefaultPasteboardDragType];
[pboard setString:[NSString stringWithFormat:@"%lu",[rows firstIndex]] forType:SPDefaultPasteboardDragType];

return YES;
}
Expand Down Expand Up @@ -332,27 +344,19 @@ - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info r
[queryString appendFormat:@"(%@)", [originalRow objectForKey:@"length"]];
}

NSString *fieldEncoding = @"";

if ([[originalRow objectForKey:@"encoding"] integerValue] > 0 && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling]) {
NSString *enc = [[encodingPopupCell itemAtIndex:[[originalRow objectForKey:@"encoding"] integerValue]] title];

NSInteger start = [enc rangeOfString:@"("].location + 1;

fieldEncoding = [enc substringWithRange:NSMakeRange(start, [enc length] - start - 1)];

NSString *fieldEncoding = [originalRow objectForKey:@"encodingName"];

if ([fieldEncoding length] && [[tableDocumentInstance serverSupport] supportsPost41CharacterSetHandling]) {
[queryString appendFormat:@" CHARACTER SET %@", fieldEncoding];
}

if (![fieldEncoding length] && [tableDataInstance tableEncoding]) {
fieldEncoding = [tableDataInstance tableEncoding];
}

if ([fieldEncoding length] && [[originalRow objectForKey:@"collation"] integerValue] > 0 && ![[originalRow objectForKey:@"binary"] integerValue]) {
NSArray *theCollations = [databaseDataInstance getDatabaseCollationsForEncoding:fieldEncoding];
NSString *col = [[theCollations objectAtIndex:[[originalRow objectForKey:@"collation"] integerValue] - 1] objectForKey:@"COLLATION_NAME"];

[queryString appendFormat:@" COLLATE %@", col];

NSString *fieldCollation = [originalRow objectForKey:@"collationName"];
if ([fieldEncoding length] && [fieldCollation length] && ![[originalRow objectForKey:@"binary"] integerValue]) {
[queryString appendFormat:@" COLLATE %@", fieldCollation];
}

// Add unsigned, zerofill, binary, not null if necessary
Expand Down

0 comments on commit f02fb78

Please sign in to comment.