diff options
-rw-r--r-- | English.lproj/Credits.rtf | 2 | ||||
-rw-r--r-- | English.lproj/DBView.xib | 53 | ||||
-rw-r--r-- | Info.plist | 4 | ||||
-rw-r--r-- | SPTableInfo.m | 30 | ||||
-rw-r--r-- | TableContent.h | 5 | ||||
-rw-r--r-- | TableContent.m | 83 | ||||
-rw-r--r-- | TableDocument.h | 7 | ||||
-rw-r--r-- | TableDocument.m | 52 | ||||
-rw-r--r-- | TableDump.h | 2 | ||||
-rw-r--r-- | TableDump.m | 538 | ||||
-rw-r--r-- | TablesList.h | 4 | ||||
-rw-r--r-- | TablesList.m | 14 |
12 files changed, 464 insertions, 330 deletions
diff --git a/English.lproj/Credits.rtf b/English.lproj/Credits.rtf index 6763d3ce..f162315c 100644 --- a/English.lproj/Credits.rtf +++ b/English.lproj/Credits.rtf @@ -50,7 +50,7 @@ City of Cairns.com (http://cityofcairns.com), for allowing Abhi and Matt to work \b GPL \b0 \ -Copyright (C) 2002-2008 Lorenz Textor, Abhi Beckert, Matt Langtree, Ben Perry. All rights reserved.\ +Copyright (C) 2002-2008 Sequel Pro and CocoaMySQL teams. All rights reserved.\ \ This program is free software; you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by\ diff --git a/English.lproj/DBView.xib b/English.lproj/DBView.xib index 6a3f5332..c8102000 100644 --- a/English.lproj/DBView.xib +++ b/English.lproj/DBView.xib @@ -1,18 +1,28 @@ <?xml version="1.0" encoding="UTF-8"?> -<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02"> +<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03"> <data> <int key="IBDocument.SystemTarget">1050</int> <string key="IBDocument.SystemVersion">9F33</string> - <string key="IBDocument.InterfaceBuilderVersion">672</string> + <string key="IBDocument.InterfaceBuilderVersion">677</string> <string key="IBDocument.AppKitVersion">949.34</string> <string key="IBDocument.HIToolboxVersion">352.00</string> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <bool key="EncodedWithXMLCoder">YES</bool> + <integer value="27"/> </object> <object class="NSArray" key="IBDocument.PluginDependencies"> <bool key="EncodedWithXMLCoder">YES</bool> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> </object> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <bool key="EncodedWithXMLCoder">YES</bool> + <object class="NSArray" key="dict.sortedKeys"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + <object class="NSMutableArray" key="dict.values"> + <bool key="EncodedWithXMLCoder">YES</bool> + </object> + </object> <object class="NSMutableArray" key="IBDocument.RootObjects" id="105205302"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSCustomObject" id="427689665"> @@ -35,7 +45,7 @@ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string> <string key="NSWindowContentMinSize">{780, 480}</string> <object class="NSView" key="NSWindowView" id="579726586"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -963,6 +973,7 @@ </object> <string key="NSFrame">{{1, 1}, {194, 396}}</string> <reference key="NSSuperview" ref="233472824"/> + <reference key="NSNextKeyView" ref="251040077"/> <reference key="NSDocView" ref="251040077"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -989,6 +1000,7 @@ </object> <string key="NSFrameSize">{196, 398}</string> <reference key="NSSuperview" ref="355288374"/> + <reference key="NSNextKeyView" ref="73685676"/> <int key="NSsFlags">530</int> <reference key="NSVScroller" ref="693168867"/> <reference key="NSHScroller" ref="656188692"/> @@ -1073,6 +1085,7 @@ </object> <string key="NSFrame">{{1, 1}, {194, 123}}</string> <reference key="NSSuperview" ref="298226231"/> + <reference key="NSNextKeyView" ref="347093764"/> <reference key="NSDocView" ref="347093764"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1099,6 +1112,7 @@ </object> <string key="NSFrame">{{0, 407}, {196, 125}}</string> <reference key="NSSuperview" ref="355288374"/> + <reference key="NSNextKeyView" ref="685057119"/> <int key="NSsFlags">530</int> <reference key="NSVScroller" ref="245346414"/> <reference key="NSHScroller" ref="353686052"/> @@ -1129,7 +1143,7 @@ <object class="NSTabViewItem" id="831053945"> <string key="NSIdentifier">source</string> <object class="NSView" key="NSView" id="461236772"> - <reference key="NSNextResponder" ref="714795046"/> + <nil key="NSNextResponder"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -1851,6 +1865,7 @@ </object> <string key="NSFrame">{{1, 17}, {626, 282}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSNextKeyView" ref="715508012"/> <reference key="NSDocView" ref="715508012"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1883,6 +1898,7 @@ </object> <string key="NSFrame">{{1, 0}, {626, 17}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSNextKeyView" ref="926883367"/> <reference key="NSDocView" ref="926883367"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1891,6 +1907,7 @@ </object> <string key="NSFrame">{{-1, 24}, {628, 300}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSNextKeyView" ref="16936123"/> <int key="NSsFlags">562</int> <reference key="NSVScroller" ref="943144555"/> <reference key="NSHScroller" ref="456666876"/> @@ -2321,6 +2338,7 @@ </object> <string key="NSFrame">{{1, 17}, {626, 138}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSNextKeyView" ref="584834515"/> <reference key="NSDocView" ref="584834515"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -2353,6 +2371,7 @@ </object> <string key="NSFrame">{{1, 0}, {626, 17}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSNextKeyView" ref="459548655"/> <reference key="NSDocView" ref="459548655"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -2361,6 +2380,7 @@ </object> <string key="NSFrame">{{-1, 22}, {628, 156}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSNextKeyView" ref="794929378"/> <int key="NSsFlags">562</int> <reference key="NSVScroller" ref="1019209947"/> <reference key="NSHScroller" ref="328951385"/> @@ -2469,7 +2489,6 @@ </object> </object> <string key="NSFrame">{{10, 7}, {638, 544}}</string> - <reference key="NSSuperview" ref="714795046"/> </object> <string key="NSLabel">Structure</string> <reference key="NSColor" ref="62854682"/> @@ -2478,7 +2497,7 @@ <object class="NSTabViewItem" id="624106058"> <string key="NSIdentifier">content</string> <object class="NSView" key="NSView" id="1013108064"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder" ref="714795046"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -3069,6 +3088,7 @@ </object> </object> <string key="NSFrame">{{10, 7}, {638, 544}}</string> + <reference key="NSSuperview" ref="714795046"/> </object> <string key="NSLabel">Content</string> <reference key="NSColor" ref="62854682"/> @@ -3933,14 +3953,14 @@ <reference key="NSTabView" ref="714795046"/> </object> </object> - <reference key="NSSelectedTabViewItem" ref="831053945"/> + <reference key="NSSelectedTabViewItem" ref="624106058"/> <reference key="NSFont" ref="26"/> <int key="NSTvFlags">134217731</int> <bool key="NSAllowTruncatedLabels">YES</bool> <bool key="NSDrawsBackground">YES</bool> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> - <reference ref="461236772"/> + <reference ref="1013108064"/> </object> </object> </object> @@ -3974,6 +3994,7 @@ </object> </object> <string key="NSFrameSize">{863, 550}</string> + <reference key="NSSuperview"/> </object> <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string> <string key="NSMinSize">{780, 502}</string> @@ -11728,6 +11749,14 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> </object> <int key="connectionID">4683</int> </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">toggleFilterField:</string> + <reference key="source" ref="392169872"/> + <reference key="destination" ref="744029762"/> + </object> + <int key="connectionID">4685</int> + </object> </object> <object class="IBMutableOrderedSet" key="objectRecords"> <object class="NSArray" key="orderedObjects"> @@ -19251,8 +19280,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> </object> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <reference ref="9"/> - <string>{{774, 505}, {863, 550}}</string> - <string>{{774, 505}, {863, 550}}</string> + <string>{{299, 286}, {863, 550}}</string> + <string>{{299, 286}, {863, 550}}</string> <reference ref="9"/> <reference ref="9"/> <string>{{62, 352}, {845, 504}}</string> @@ -19665,7 +19694,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> </object> </object> <nil key="sourceID"/> - <int key="maxID">4683</int> + <int key="maxID">4685</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <object class="NSMutableArray" key="referencedPartialClassDescriptions"> @@ -19877,6 +19906,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>setCompareTypes:</string> <string>showAll:</string> <string>stepLimitRows:</string> + <string>toggleFilterField:</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -19893,6 +19923,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>id</string> <string>id</string> <string>id</string> + <string>id</string> </object> </object> <object class="NSMutableDictionary" key="outlets"> @@ -44,11 +44,11 @@ <key>CFBundleShortVersionString</key> <string>0.9.3</string> <key>CFBundleVersion</key> - <string>236</string> + <string>254</string> <key>NSAppleScriptEnabled</key> <string>YES</string> <key>NSHumanReadableCopyright</key> - <string>0.9.3 (236)</string> + <string>0.9.3 (254)</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> diff --git a/SPTableInfo.m b/SPTableInfo.m index 5e777429..b2b81c05 100644 --- a/SPTableInfo.m +++ b/SPTableInfo.m @@ -114,27 +114,37 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn // Process result theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject]; - // Add the table name to the infoTable - [info addObject:[NSString stringWithFormat:@"name: %@", [tableListInstance table]]]; - // Check for "Create_time" == NULL if (![[theRow objectForKey:@"Create_time"] isNSNull]) { // Setup our data formatter - NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; - [dateFormatter setDateStyle:NSDateFormatterShortStyle]; - [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; + NSDateFormatter *createDateFormatter = [[[NSDateFormatter alloc] init] autorelease]; + [createDateFormatter setDateStyle:NSDateFormatterShortStyle]; + [createDateFormatter setTimeStyle:NSDateFormatterNoStyle]; - // Convert our string dates from the results to NSDates. + // Convert our string date from the result to an NSDate. NSDate *create_date = [NSDate dateWithNaturalLanguageString:[theRow objectForKey:@"Create_time"]]; + + // Add the creation date to the infoTable + [info addObject:[NSString stringWithFormat:@"created: %@", [createDateFormatter stringFromDate:create_date]]]; + } + + // Check for "Update_time" == NULL - InnoDB tables don't have an update time + if (![[theRow objectForKey:@"Update_time"] isNSNull]) { + // Setup our data formatter + NSDateFormatter *updateDateFormatter = [[[NSDateFormatter alloc] init] autorelease]; + [updateDateFormatter setDateStyle:NSDateFormatterShortStyle]; + [updateDateFormatter setTimeStyle:NSDateFormatterNoStyle]; + + // Convert our string date from the result to an NSDate. NSDate *update_date = [NSDate dateWithNaturalLanguageString:[theRow objectForKey:@"Update_time"]]; - // Add the create date and update date to the infoTable - [info addObject:[NSString stringWithFormat:@"created: %@", [dateFormatter stringFromDate:create_date]]]; - [info addObject:[NSString stringWithFormat:@"updated: %@", [dateFormatter stringFromDate:update_date]]]; + // Add the update date to the infoTable + [info addObject:[NSString stringWithFormat:@"updated: %@", [updateDateFormatter stringFromDate:update_date]]]; } [info addObject:[NSString stringWithFormat:@"rows: %@", [theRow objectForKey:@"Rows"]]]; [info addObject:[NSString stringWithFormat:@"size: %@", [self sizeFromBytes:[[theRow objectForKey:@"Data_length"] intValue]]]]; + [info addObject:[NSString stringWithFormat:@"encoding: %@", [[[theRow objectForKey:@"Collation"] componentsSeparatedByString:@"_"] objectAtIndex:0]]]; [info addObject:[NSString stringWithFormat:@"auto_increment: %@", [theRow objectForKey:@"Auto_increment"]]]; // Notify that we've finished performing the query diff --git a/TableContent.h b/TableContent.h index 64a57060..54ad2481 100644 --- a/TableContent.h +++ b/TableContent.h @@ -78,6 +78,7 @@ - (IBAction)reloadTableValues:(id)sender; - (IBAction)filterTable:(id)sender; - (IBAction)showAll:(id)sender; +- (IBAction)toggleFilterField:(id)sender; //edit methods - (IBAction)addRow:(id)sender; @@ -131,8 +132,4 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn //textView delegate methods - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector; -//last but not least -- (id)init; -- (void)dealloc; - @end diff --git a/TableContent.m b/TableContent.m index 1e5e898a..c3238a3c 100644 --- a/TableContent.m +++ b/TableContent.m @@ -420,7 +420,8 @@ NSString *queryString; int i; - if ([argument length] == 0) { + // If the filter field is empty or the selected filter is not looking for NULLs or 'not' NULLs, then don't allow filtering. + if (([argument length] == 0) && (![[[compareField selectedItem] title] hasSuffix:@"NULL"])) { [argument release]; [self showAll:sender]; return; @@ -430,6 +431,7 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; BOOL doQuote = YES; + BOOL ignoreArgument = NO; if ( ![compareType isEqualToString:@""] ) { if ( [compareType isEqualToString:@"string"] ) { @@ -454,6 +456,16 @@ doQuote = NO; [argument setString:[[@"(" stringByAppendingString:argument] stringByAppendingString:@")"]]; break; + case 5: + compareOperator = @"IS NULL"; + doQuote = NO; + ignoreArgument = YES; + break; + case 6: + compareOperator = @"IS NOT NULL"; + doQuote = NO; + ignoreArgument = YES; + break; } } else if ( [compareType isEqualToString:@"number"] ) { //number comparision @@ -481,6 +493,16 @@ doQuote = NO; [argument setString:[[@"(" stringByAppendingString:argument] stringByAppendingString:@")"]]; break; + case 7: + compareOperator = @"IS NULL"; + doQuote = NO; + ignoreArgument = YES; + break; + case 8: + compareOperator = @"IS NOT NULL"; + doQuote = NO; + ignoreArgument = YES; + break; } } else if ( [compareType isEqualToString:@"date"] ) { //date comparision @@ -503,12 +525,19 @@ case 5: compareOperator = @">="; break; + case 6: + compareOperator = @"IS NULL"; + doQuote = NO; + ignoreArgument = YES; + break; + case 7: + compareOperator = @"IS NOT NULL"; + doQuote = NO; + ignoreArgument = YES; + break; } } - // queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@` WHERE `%@` %@ '%@'", - // [self fieldListForQuery], selectedTable, [fieldField titleOfSelectedItem], - // compareOperator, argument]; if (doQuote) { //escape special characters for ( i = 0 ; i < [argument length] ; i++ ) { @@ -524,7 +553,7 @@ } else { queryString = [NSString stringWithFormat:@"SELECT %@ FROM `%@` WHERE `%@` %@ %@", [self fieldListForQuery], selectedTable, [fieldField titleOfSelectedItem], - compareOperator, argument]; + compareOperator, (ignoreArgument) ? @"" : argument]; } if ( sortField ) { // queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@" ORDER BY `%@`", sortField]]; @@ -544,7 +573,7 @@ NSLog(@"ERROR: unknown compare type %@", compareType); queryString = @""; } - + theResult = [mySQLConnection queryString:queryString]; [filteredResult setArray:[self fetchResultAsArray:theResult]]; @@ -570,6 +599,15 @@ [countText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d rows in table", @"text showing how many rows are in the result"), numRows]]; } +/** + * Enables or disables the filter input field based on the selected filter type. + */ +- (IBAction)toggleFilterField:(id)sender +{ + // If the user is filtering for NULLs then disabled the filter field, otherwise enable it. + [argumentField setEnabled:(![[[compareField selectedItem] title] hasSuffix:@"NULL"])]; +} + //edit methods - (IBAction)addRow:(id)sender @@ -921,28 +959,24 @@ } } -- (IBAction)setCompareTypes:(id)sender -/* - sets the compare types for the filter and the appropriate formatter for the textField +/** + * Sets the compare types for the filter and the appropriate formatter for the textField */ +- (IBAction)setCompareTypes:(id)sender { NSArray *stringFields = [NSArray arrayWithObjects:@"varstring", @"string", @"tinyblob", @"blob", @"mediumblob", @"longblob", @"set", @"enum", nil]; - NSArray *stringTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"contains", @"popup menuitem for field CONTAINS value"), NSLocalizedString(@"contains not", @"popup menuitem for field CONTAINS NOT value"), @"IN", nil]; + NSArray *stringTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"contains", @"popup menuitem for field CONTAINS value"), NSLocalizedString(@"contains not", @"popup menuitem for field CONTAINS NOT value"), @"IN", nil]; NSArray *numberFields = [NSArray arrayWithObjects:@"tiny", @"short", @"long", @"int24", @"longlong", @"decimal", @"float", @"double", nil]; - NSArray *numberTypes = [NSArray arrayWithObjects:@"=", @"≠", @">", @"<", @"≥", @"≤", @"IN", nil]; - NSArray *dateFields = [NSArray arrayWithObjects:@"timestamp", @"date", @"time", @"datetime", @"year", nil]; - NSArray *dateTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"older than", @"popup menuitem for field OLDER THAN value"), NSLocalizedString(@"younger than", @"popup menuitem for field YOUNGER THAN value"), NSLocalizedString(@"older than or equal to", @"popup menuitem for field OLDER THAN OR EQUAL TO value"), NSLocalizedString(@"younger than or equal to", @"popup menuitem for field YOUNGER THAN OR EQUAL TO value"), nil]; - NSString *fieldType = [NSString stringWithString:[fieldTypes objectAtIndex:[[fieldField selectedItem] tag]]]; - // NSNumberFormatter *numberFormatter; - int i; + NSArray *numberTypes = [NSArray arrayWithObjects:@"=", @"≠", @">", @"<", @"≥", @"≤", @"IN", nil]; + NSArray *dateFields = [NSArray arrayWithObjects:@"timestamp", @"date", @"time", @"datetime", @"year", nil]; + NSArray *dateTypes = [NSArray arrayWithObjects:NSLocalizedString(@"is", @"popup menuitem for field IS value"), NSLocalizedString(@"is not", @"popup menuitem for field IS NOT value"), NSLocalizedString(@"older than", @"popup menuitem for field OLDER THAN value"), NSLocalizedString(@"younger than", @"popup menuitem for field YOUNGER THAN value"), NSLocalizedString(@"older than or equal to", @"popup menuitem for field OLDER THAN OR EQUAL TO value"), NSLocalizedString(@"younger than or equal to", @"popup menuitem for field YOUNGER THAN OR EQUAL TO value"), nil]; + NSString *fieldType = [NSString stringWithString:[fieldTypes objectAtIndex:[[fieldField selectedItem] tag]]]; - // numberFormatter = [[[NSNumberFormatter alloc] init] autorelease]; - // [numberFormatter setFormat:@"0.####################"]; + int i; [compareField removeAllItems]; - // [argumentField setStringValue:@""]; - //why do we get "string" for enum fields? (error in framework?) + // Why do we get "string" for enum fields? (error in framework?) if ( [stringFields containsObject:fieldType] ) { [compareField addItemsWithTitles:stringTypes]; compareType = @"string"; @@ -976,10 +1010,17 @@ NSLog(@"ERROR: unknown type for comparision: %@", fieldType); } + // Add IS NULL and IS NOT NULL as they should always be available + [compareField addItemWithTitle:@"IS NULL"]; + [compareField addItemWithTitle:@"IS NOT NULL"]; + for ( i = 0 ; i < [compareField numberOfItems] ; i++ ) { [[compareField itemAtIndex:i] setTag:i]; } - + + // Update the argumentField enabled state + [self toggleFilterField:self]; + // set focus on argumentField [argumentField selectText:self]; } diff --git a/TableDocument.h b/TableDocument.h index 2a54a37c..62798b2d 100644 --- a/TableDocument.h +++ b/TableDocument.h @@ -81,7 +81,6 @@ NSMutableArray *favorites; NSArray *variables; NSString *selectedDatabase; - NSString *selectedFavorite; NSString *mySQLVersion; NSUserDefaults *prefs; @@ -135,7 +134,8 @@ //encoding methods - (void)setEncoding:(NSString *)encoding; -- (void)detectEncoding; +- (void)detectDatabaseEncoding; +- (void)detectTableEncodingForTable:(NSString *)table; - (IBAction)chooseEncoding:(id)sender; - (BOOL)supportsEncoding; - (void)updateEncodingMenuWithSelectedEncoding:(NSString *)encoding; @@ -215,9 +215,6 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex; -//for freeing up memory -- (void)dealloc; - @end extern NSString *TableDocumentFavoritesControllerSelectionIndexDidChange; diff --git a/TableDocument.m b/TableDocument.m index 59aad2e0..2dcb5f1a 100644 --- a/TableDocument.m +++ b/TableDocument.m @@ -182,7 +182,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa // set encoding NSString *encodingName = [prefs objectForKey:@"encoding"]; if ( [encodingName isEqualToString:@"Autodetect"] ) { - [self detectEncoding]; + [self detectDatabaseEncoding]; } else { [self setEncoding:[self mysqlEncodingFromDisplayEncoding:encodingName]]; } @@ -260,9 +260,6 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [portField setStringValue:[self valueForKeyPath:@"selectedFavorite.port"]]; [databaseField setStringValue:[self valueForKeyPath:@"selectedFavorite.database"]]; [passwordField setStringValue:[self selectedFavoritePassword]]; - - [selectedFavorite release]; - selectedFavorite = [[favoritesButton titleOfSelectedItem] retain]; } /** @@ -368,7 +365,7 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa [self willChangeValueForKey:@"favorites"]; // write favorites and password - NSDictionary *newFavorite = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, nil] + NSMutableDictionary *newFavorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, nil] forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", nil]]; [favorites addObject:newFavorite]; @@ -378,9 +375,6 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]]; } - // select new favorite - selectedFavorite = [favoriteName retain]; - [self didChangeValueForKey:@"favorites"]; [favoritesController setSelectedObjects:[NSArray arrayWithObject:newFavorite]]; } @@ -638,14 +632,15 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa { // set encoding of connection and client [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", mysqlEncoding]]; + if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [mySQLConnection setEncoding:[CMMCPConnection encodingForMySQLEncoding:[mysqlEncoding UTF8String]]]; [_encoding autorelease]; _encoding = [mysqlEncoding retain]; } else { - [self detectEncoding]; + [self detectDatabaseEncoding]; } - + // update the selected menu item [self updateEncodingMenuWithSelectedEncoding:[self encodingNameFromMySQLEncoding:mysqlEncoding]]; @@ -748,9 +743,9 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa /** * Autodetect the connection encoding and select the relevant encoding menu item in Database -> Database Encoding */ -- (void)detectEncoding +- (void)detectDatabaseEncoding { - // mysql > 4.0 + // MySQL > 4.0 id mysqlEncoding = [[[mySQLConnection queryString:@"SHOW VARIABLES LIKE 'character_set_connection'"] fetchRowAsDictionary] objectForKey:@"Value"]; _supportsEncoding = (mysqlEncoding != nil); @@ -761,9 +756,10 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa mysqlEncoding = [[[mySQLConnection queryString:@"SHOW VARIABLES LIKE 'character_set'"] fetchRowAsDictionary] objectForKey:@"Value"]; } if ( !mysqlEncoding ) { // older version? -> set encoding to mysql default encoding latin1 - NSLog(@"error: no character encoding found, mysql version is %@", [self mySQLVersion]); + NSLog(@"Error: no character encoding found, mysql version is %@", [self mySQLVersion]); mysqlEncoding = @"latin1"; } + [mySQLConnection setEncoding:[CMMCPConnection encodingForMySQLEncoding:[mysqlEncoding cString]]]; // save the encoding @@ -775,7 +771,29 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa } /** - * when sent by an NSMenuItem, will set the encoding based on the title of the menu item + * Detects and sets the character set encoding of the supplied table name. + */ +- (void)detectTableEncodingForTable:(NSString *)table; +{ + NSString *tableCollation = [[[mySQLConnection queryString:[NSString stringWithFormat:@"SHOW TABLE STATUS WHERE NAME = '%@'", table]] fetchRowAsDictionary] objectForKey:@"Collation"]; + + if (tableCollation != nil) { + // Split up the collation string so we can get the encoding + NSArray *encodingComponents = [tableCollation componentsSeparatedByString:@"_"]; + + if ([encodingComponents count] > 0) { + NSString *tableEncoding = [encodingComponents objectAtIndex:0]; + + [self setEncoding:tableEncoding]; + } + } + else { + NSLog(@"Error: unable to determine table collation and thus character encoding for table '%@'", table); + } +} + +/** + * When sent by an NSMenuItem, will set the encoding based on the title of the menu item */ - (IBAction)chooseEncoding:(id)sender { @@ -1428,7 +1446,6 @@ NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFa // [tableWindow makeKeyAndOrderFront:self]; prefs = [[NSUserDefaults standardUserDefaults] retain]; - selectedFavorite = [[NSString alloc] initWithString:NSLocalizedString(@"Custom", @"menu item for custom connection")]; //register for notifications [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willPerformQuery:) @@ -1593,6 +1610,10 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn - (IBAction)terminate:(id)sender { [[NSApp orderedDocuments] makeObjectsPerformSelector:@selector(cancelConnectSheet:) withObject:nil]; + + // Save the favourites - commits any unsaved changes ie favourite renames + [prefs setObject:[self favorites] forKey:@"favorites"]; + [NSApp terminate:sender]; } @@ -1603,7 +1624,6 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn [favorites release]; [variables release]; [selectedDatabase release]; - [selectedFavorite release]; [mySQLVersion release]; [prefs release]; diff --git a/TableDump.h b/TableDump.h index a382e082..22b123af 100644 --- a/TableDump.h +++ b/TableDump.h @@ -89,6 +89,7 @@ NSMutableArray *tables; NSArray *importArray; NSMutableArray *fieldMappingArray; + NSMutableArray *fieldMappingButtonOptions; int currentRow; NSString *savePath; NSString *openPath; @@ -114,6 +115,7 @@ - (IBAction)changeTable:(id)sender; - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo; - (void)setupFieldMappingArray; +- (void)updateFieldMappingButtonCell; - (NSArray *)arrayForCSV:(NSString *)csv terminatedBy:(NSString *)terminated enclosedBy:(NSString *)enclosed escapedBy:(NSString *)escaped lineEnds:(NSString *)lineEnds; - (NSArray *)arrayForString:(NSString *)string enclosed:(NSString *)enclosed diff --git a/TableDump.m b/TableDump.m index a40458d0..277ed3e7 100644 --- a/TableDump.m +++ b/TableDump.m @@ -110,55 +110,55 @@ [savePanel setAccessoryView:exportDumpView]; contextInfo = @"exportDump"; break; - - // Export the full resultset for the currently selected table to a file in CSV format + + // Export the full resultset for the currently selected table to a file in CSV format case 6: file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance table]]; [savePanel setAccessoryView:exportCSVView]; contextInfo = @"exportTableContentAsCSV"; break; - - // Export the full resultset for the currently selected table to a file in XML format + + // Export the full resultset for the currently selected table to a file in XML format case 7: file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance table]]; contextInfo = @"exportTableContentAsXML"; break; - - // Export the current "browse" view to a file in CSV format + + // Export the current "browse" view to a file in CSV format case 8: file = [NSString stringWithFormat:@"%@ view.csv", [tableDocumentInstance table]]; [savePanel setAccessoryView:exportCSVView]; contextInfo = @"exportBrowseViewAsCSV"; break; - - // Export the current "browse" view to a file in XML format + + // Export the current "browse" view to a file in XML format case 9: file = [NSString stringWithFormat:@"%@ view.xml", [tableDocumentInstance table]]; contextInfo = @"exportBrowseViewAsXML"; break; - - // Export the current custom query result set to a file in CSV format + + // Export the current custom query result set to a file in CSV format case 10: file = @"customresult.csv"; [savePanel setAccessoryView:exportCSVView]; contextInfo = @"exportCustomResultAsCSV"; break; - - // Export the current custom query result set to a file in XML format + + // Export the current custom query result set to a file in XML format case 11: file = @"customresult.xml"; contextInfo = @"exportCustomResultAsXML"; break; - - // Export multiple tables to a file in CSV format + + // Export multiple tables to a file in CSV format case 12: [self reloadTables:self]; file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance database]]; [savePanel setAccessoryView:exportMultipleCSVView]; contextInfo = @"exportMultipleTablesAsCSV"; break; - - // Export multiple tables to a file in XML format + + // Export multiple tables to a file in XML format case 13: [self reloadTables:self]; file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance database]]; @@ -173,8 +173,8 @@ // Open the savePanel [savePanel beginSheetForDirectory:[prefs objectForKey:@"savePath"] - file:file modalForWindow:tableWindow modalDelegate:self - didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; + file:file modalForWindow:tableWindow modalDelegate:self + didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } /* @@ -189,10 +189,10 @@ if ( returnCode != NSOKButton ) return; - + // Save path to preferences [prefs setObject:[sheet directory] forKey:@"savePath"]; - + // Error if the file already exists and is not writable, and get a fileHandle to it. if ( [[NSFileManager defaultManager] fileExistsAtPath:[sheet filename]] ) { if ( ![[NSFileManager defaultManager] isWritableFileAtPath:[sheet filename]] @@ -204,21 +204,21 @@ // Truncate the file to zero bytes [fileHandle truncateFileAtOffset:0]; - - // Otherwise attempt to create a file + + // Otherwise attempt to create a file } else { if ( ![[NSFileManager defaultManager] createFileAtPath:[sheet filename] contents:[NSData data] attributes:nil] ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); return; } - + // Retrieve a filehandle for the file, attempting to delete it on failure. fileHandle = [NSFileHandle fileHandleForWritingAtPath:[sheet filename]]; if ( !fileHandle ) { [[NSFileManager defaultManager] removeFileAtPath:[sheet filename] handler:nil]; NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, - NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); + NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); return; } } @@ -226,62 +226,62 @@ // Export the tables selected in the MySQL export sheet to a file if ( [contextInfo isEqualToString:@"exportDump"] ) { success = [self dumpSelectedTablesAsSqlToFileHandle:fileHandle]; - - // Export the full resultset for the currently selected table to a file in CSV format + + // Export the full resultset for the currently selected table to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportTableContentAsCSV"] ) { success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"csv"]; - - // Export the full resultset for the currently selected table to a file in XML format + + // Export the full resultset for the currently selected table to a file in XML format } else if ( [contextInfo isEqualToString:@"exportTableContentAsXML"] ) { success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"xml"]; - - // Export the current "browse" view to a file in CSV format + + // Export the current "browse" view to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportBrowseViewAsCSV"] ) { success = [self writeCsvForArray:[tableContentInstance currentResult] orQueryResult:nil toFileHandle:fileHandle - outputFieldNames:[exportFieldNamesSwitch state] + outputFieldNames:[exportFieldNamesSwitch state] terminatedBy:[exportFieldsTerminatedField stringValue] - enclosedBy:[exportFieldsEnclosedField stringValue] - escapedBy:[exportFieldsEscapedField stringValue] - lineEnds:[exportLinesTerminatedField stringValue] - silently:NO]; - - // Export the current "browse" view to a file in XML format + enclosedBy:[exportFieldsEnclosedField stringValue] + escapedBy:[exportFieldsEscapedField stringValue] + lineEnds:[exportLinesTerminatedField stringValue] + silently:NO]; + + // Export the current "browse" view to a file in XML format } else if ( [contextInfo isEqualToString:@"exportBrowseViewAsXML"] ) { success = [self writeXmlForArray:[tableContentInstance currentResult] orQueryResult:nil toFileHandle:fileHandle - tableName:[tableDocumentInstance table] - withHeader:YES - silently:NO]; - - // Export the current custom query result set to a file in CSV format + tableName:[tableDocumentInstance table] + withHeader:YES + silently:NO]; + + // Export the current custom query result set to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportCustomResultAsCSV"] ) { success = [self writeCsvForArray:[customQueryInstance currentResult] orQueryResult:nil toFileHandle:fileHandle - outputFieldNames:[exportFieldNamesSwitch state] + outputFieldNames:[exportFieldNamesSwitch state] terminatedBy:[exportFieldsTerminatedField stringValue] - enclosedBy:[exportFieldsEnclosedField stringValue] - escapedBy:[exportFieldsEscapedField stringValue] - lineEnds:[exportLinesTerminatedField stringValue] - silently:NO]; - - // Export the current custom query result set to a file in XML format + enclosedBy:[exportFieldsEnclosedField stringValue] + escapedBy:[exportFieldsEscapedField stringValue] + lineEnds:[exportLinesTerminatedField stringValue] + silently:NO]; + + // Export the current custom query result set to a file in XML format } else if ( [contextInfo isEqualToString:@"exportCustomResultAsXML"] ) { success = [self writeXmlForArray:[customQueryInstance currentResult] orQueryResult:nil toFileHandle:fileHandle - tableName:@"custom" - withHeader:YES - silently:NO]; - - // Export multiple tables to a file in CSV format + tableName:@"custom" + withHeader:YES + silently:NO]; + + // Export multiple tables to a file in CSV format } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsCSV"] ) { success = [self exportSelectedTablesToFileHandle:fileHandle usingFormat:@"csv"]; - - // Export multiple tables to a file in XML format + + // Export multiple tables to a file in XML format } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsXML"] ) { success = [self exportSelectedTablesToFileHandle:fileHandle usingFormat:@"xml"]; - - // Unknown operation + + // Unknown operation } else { NSLog(@"Unknown export operation: %@", [contextInfo description]); return; @@ -289,10 +289,10 @@ // Close the file handle [fileHandle closeFile]; - + if ( !success ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, - NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); + NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); } // Export finished Growl notification @@ -327,9 +327,7 @@ } - (IBAction)changeTable:(id)sender -{ - NSPopUpButtonCell *buttonCell = [[NSPopUpButtonCell alloc] init]; - +{ [tableListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[tablesListInstance tables] indexOfObject:[fieldMappingPopup titleOfSelectedItem]]] byExtendingSelection:NO]; //set up tableView @@ -340,14 +338,7 @@ [rowUpButton setEnabled:([importArray count] > 1)]; [recordCountLabel setStringValue:[NSString stringWithFormat:@"%i of %i records", currentRow+1, [importArray count]]]; - //set up tableView buttons - [buttonCell setControlSize:NSSmallControlSize]; - [buttonCell setFont:[NSFont labelFontOfSize:[NSFont smallSystemFontSize]]]; - [buttonCell setBordered:NO]; - [buttonCell addItemWithTitle:NSLocalizedString(@"Do not import", @"text for csv import drop downs")]; - [buttonCell addItemsWithTitles:[importArray objectAtIndex:currentRow]]; - - [[fieldMappingTableView tableColumnWithIdentifier:@"value"] setDataCell:buttonCell]; + [self updateFieldMappingButtonCell]; [fieldMappingTableView reloadData]; } @@ -403,7 +394,7 @@ //import dump file NSArray *queries; int i; - + //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow @@ -417,7 +408,7 @@ //get array with an object for each mysql-query queries = [self splitQueries:dumpFile]; - + [singleProgressBar stopAnimation:self]; [singleProgressBar setUsesThreadedAnimation:NO]; [singleProgressBar setIndeterminate:NO]; @@ -452,9 +443,9 @@ [errorsSheet orderOut:nil]; } - //////////////// - // IMPORT CSV // - //////////////// + //////////////// + // IMPORT CSV // + //////////////// } else if ( [fileType isEqualToString:@"CSV"] ) { //import csv file @@ -518,11 +509,10 @@ [buttonCell setControlSize:NSSmallControlSize]; [buttonCell setFont:[NSFont labelFontOfSize:[NSFont smallSystemFontSize]]]; [buttonCell setBordered:NO]; - [buttonCell addItemWithTitle:NSLocalizedString(@"Do not import", @"text for csv import drop downs")]; - [buttonCell addItemsWithTitles:[tableSourceInstance fieldNames]]; + [[fieldMappingTableView tableColumnWithIdentifier:@"value"] setDataCell:buttonCell]; + [self updateFieldMappingButtonCell]; + [fieldMappingTableView reloadData]; - [[fieldMappingTableView tableColumnWithIdentifier:@"1"] setDataCell:buttonCell]; - // show fieldMapping sheet [NSApp beginSheet:fieldMappingSheet modalForWindow:tableWindow @@ -574,7 +564,7 @@ if ([[fieldMappingArray objectAtIndex:j] intValue] > 0) { if ( [fValues length] ) [fValues appendString:@","]; - + if ([[[importArray objectAtIndex:i] objectAtIndex:([[fieldMappingArray objectAtIndex:j] intValue] - 1)] isMemberOfClass:[NSNull class]] ) { [fValues appendString:@"NULL"]; } else { @@ -637,15 +627,15 @@ if ( fieldMappingArray ) { -// for ( i = 0 ; i < [fieldMappingArray count] ; i++ ) { -// -// if ( [[[importArray objectAtIndex:currentRow] objectAtIndex:i] isKindOfClass:[NSNull class]] ) { -// [fieldMappingArray replaceObjectAtIndex:i withObject:0]; -// -// } else { -// [fieldMappingArray replaceObjectAtIndex:i withObject:[[importArray objectAtIndex:currentRow] objectAtIndex:0]]; -// } -// } + // for ( i = 0 ; i < [fieldMappingArray count] ; i++ ) { + // + // if ( [[[importArray objectAtIndex:currentRow] objectAtIndex:i] isKindOfClass:[NSNull class]] ) { + // [fieldMappingArray replaceObjectAtIndex:i withObject:0]; + // + // } else { + // [fieldMappingArray replaceObjectAtIndex:i withObject:[[importArray objectAtIndex:currentRow] objectAtIndex:0]]; + // } + // } } else { fieldMappingArray = [NSMutableArray array]; @@ -656,7 +646,7 @@ } else { value = 0; } - + [fieldMappingArray addObject:[NSNumber numberWithInt:value]]; } @@ -666,6 +656,22 @@ [fieldMappingTableView reloadData]; } +/* + * Update the NSButtonCell items for use in the field mapping display + */ +- (void)updateFieldMappingButtonCell +{ + int i; + + [fieldMappingButtonOptions setArray:[importArray objectAtIndex:currentRow]]; + for (i = 0; i < [fieldMappingButtonOptions count]; i++) { + if ([[fieldMappingButtonOptions objectAtIndex:i] isNSNull]) { + [fieldMappingButtonOptions replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%i. %@", i+1, [prefs objectForKey:@"nullValue"]]]; + } else { + [fieldMappingButtonOptions replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%i. %@", i+1, [fieldMappingButtonOptions objectAtIndex:i]]]; + } + } +} - (IBAction)stepRow:(id)sender /* @@ -677,6 +683,7 @@ } else { currentRow++; } + [self updateFieldMappingButtonCell]; //-----------[self setupFieldMappingArray]; [fieldMappingTableView reloadData]; @@ -722,23 +729,23 @@ // Open the progress sheet [NSApp beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self - didEndSelector:nil contextInfo:nil]; - + modalForWindow:tableWindow modalDelegate:self + didEndSelector:nil contextInfo:nil]; + // Copy over the selected table names into a table in preparation for iteration for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { [selectedTables addObject:[NSString stringWithString:[[tables objectAtIndex:i] objectAtIndex:1]]]; } } - + // Add the dump header to the dump file. [headerString setString:@"# Sequel Pro dump\n"]; [headerString appendString:[NSString stringWithFormat:@"# Version %@\n", - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [headerString appendString:@"# http://code.google.com/p/sequel-pro\n#\n"]; [headerString appendString:[NSString stringWithFormat:@"# Host: %@ (MySQL %@)\n", - [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; + [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [headerString appendString:[NSString stringWithFormat:@"# Database: %@\n", [tableDocumentInstance database]]]; [headerString appendString:[NSString stringWithFormat:@"# Generation Time: %@\n", [NSDate date]]]; [headerString appendString:@"# ************************************************************\n\n"]; @@ -747,7 +754,7 @@ // Loop through the selected tables for ( i = 0 ; i < [selectedTables count] ; i++ ) { lastProgressValue = 0; - + // Update the progress text and reset the progress bar to indeterminate status while fetching data tableName = [selectedTables objectAtIndex:i]; [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %i of %i (%@): Fetching data...", @"text showing that app is fetching data for table dump"), (i+1), [selectedTables count], tableName]]; @@ -755,17 +762,17 @@ [singleProgressBar setIndeterminate:YES]; [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar startAnimation:self]; - + // Add the name of table [fileHandle writeData:[[NSString stringWithFormat:@"# Dump of table %@\n# ------------------------------------------------------------\n\n", tableName] - dataUsingEncoding:connectionEncoding]]; - - + dataUsingEncoding:connectionEncoding]]; + + // Add a "drop table" command if specified in the export dialog if ( [addDropTableSwitch state] == NSOnState ) [fileHandle writeData:[[NSString stringWithFormat:@"DROP TABLE IF EXISTS `%@`;\n\n", tableName] - dataUsingEncoding:connectionEncoding]]; - + dataUsingEncoding:connectionEncoding]]; + // Add the create syntax for the table if specified in the export dialog if ( [addCreateTableSwitch state] == NSOnState ) { queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", tableName]]; @@ -784,7 +791,7 @@ } } } - + // Add the table content if required if ( [addTableContentSwitch state] == NSOnState ) { queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; @@ -799,56 +806,56 @@ [singleProgressBar setIndeterminate:NO]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; - + if (rowCount) { [queryResult dataSeek:0]; queryLength = 0; - + // Construct the start of the insertion command [fileHandle writeData:[[NSString stringWithFormat:@"INSERT INTO `%@` (`%@`)\nVALUES\n\t(", - tableName, [fieldNames componentsJoinedByString:@"`,`"]] dataUsingEncoding:connectionEncoding]]; + tableName, [fieldNames componentsJoinedByString:@"`,`"]] dataUsingEncoding:connectionEncoding]]; // Iterate through the rows to construct a VALUES group for each for ( j = 0 ; j < rowCount ; j++ ) { theRow = [queryResult fetchRowAsArray]; [sqlString setString:@""]; - + // Update the progress bar [singleProgressBar setDoubleValue:((j+1)*100/rowCount)]; if ((int)[singleProgressBar doubleValue] > lastProgressValue) { lastProgressValue = (int)[singleProgressBar doubleValue]; [singleProgressBar displayIfNeeded]; } - + for ( t = 0 ; t < [theRow count] ; t++ ) { - + // Add NULL values directly to the output row if ( [[theRow objectAtIndex:t] isMemberOfClass:[NSNull class]] ) { [sqlString appendString:@"NULL"]; - - // Add data types directly as hex data + + // Add data types directly as hex data } else if ( [[theRow objectAtIndex:t] isKindOfClass:[NSData class]] ) { [sqlString appendString:@"X'"]; [sqlString appendString:[mySQLConnection prepareBinaryData:[theRow objectAtIndex:t]]]; [sqlString appendString:@"'"]; - + } else { [cellValue setString:[[theRow objectAtIndex:t] description]]; // Add empty strings as a pair of quotes if ([cellValue length] == 0) { [sqlString appendString:@"''"]; - + } else { - + // Test whether this cell contains a number sqlNumericTester = [NSScanner scannerWithString:cellValue]; // If it does contain a number, add the number directly if ([sqlNumericTester scanFloat:nil] && [sqlNumericTester isAtEnd]) { [sqlString appendString:cellValue]; - - // Otherwise add a quoted string with special characters escaped + + // Otherwise add a quoted string with special characters escaped } else { [sqlString appendString:@"'"]; [sqlString appendString:[mySQLConnection prepareString:cellValue]]; @@ -860,16 +867,16 @@ // Add the field separator if this isn't the last cell in the row if (t != [theRow count] - 1) [sqlString appendString:@","]; } - + queryLength += [sqlString length]; // Close this VALUES group and set up the next one if appropriate if (j != rowCount - 1) { - + // Add a new INSERT starter command every ~250k of data. if (queryLength > 250000) { [sqlString appendString:[NSString stringWithFormat:@");\n\nINSERT INTO `%@` (`%@`)\nVALUES\n\t(", - tableName, [fieldNames componentsJoinedByString:@"`,`"]]]; + tableName, [fieldNames componentsJoinedByString:@"`,`"]]]; queryLength = 0; } else { [sqlString appendString:@"),\n\t("]; @@ -881,24 +888,24 @@ // Write this row to the file [fileHandle writeData:[sqlString dataUsingEncoding:connectionEncoding]]; } - + // Complete the command [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:connectionEncoding]]; - + if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; if ( [addErrorsSwitch state] == NSOnState ) { [fileHandle writeData:[[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]] - dataUsingEncoding:connectionEncoding]]; + dataUsingEncoding:connectionEncoding]]; } } } } - + // Add an additional separator between tables [fileHandle writeData:[[NSString stringWithString:@"\n\n"] dataUsingEncoding:connectionEncoding]]; } - + // Close the progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; @@ -922,7 +929,7 @@ Takes an array and writes it in CSV format to the supplied NSFileHandle */ - (BOOL)writeCsvForArray:(NSArray *)array orQueryResult:(CMMCPResult *)queryResult toFileHandle:(NSFileHandle *)fileHandle outputFieldNames:(BOOL)outputFieldNames terminatedBy:(NSString *)fieldSeparatorString - enclosedBy:(NSString *)enclosingString escapedBy:(NSString *)escapeString lineEnds:(NSString *)lineEndString silently:(BOOL)silently; + enclosedBy:(NSString *)enclosingString escapedBy:(NSString *)escapeString lineEnds:(NSString *)lineEndString silently:(BOOL)silently; { NSStringEncoding tableEncoding = [CMMCPConnection encodingForMySQLEncoding:[[tableDocumentInstance encoding] cString]]; NSMutableString *csvCell = [NSMutableString string]; @@ -935,31 +942,31 @@ BOOL quoteFieldSeparators = [enclosingString isEqualToString:@""]; BOOL csvCellIsNumeric; int i, j, startingRow, totalRows, progressBarWidth, lastProgressValue; - + if (queryResult != nil && [queryResult numOfRows]) [queryResult dataSeek:0]; - + // Detect and restore special characters being used as terminating or line end strings NSMutableString *tempSeparatorString = [NSMutableString stringWithString:fieldSeparatorString]; [tempSeparatorString replaceOccurrencesOfString:@"\\t" withString:@"\t" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; [tempSeparatorString replaceOccurrencesOfString:@"\\n" withString:@"\n" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; [tempSeparatorString replaceOccurrencesOfString:@"\\r" withString:@"\r" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; fieldSeparatorString = [NSString stringWithString:tempSeparatorString]; NSMutableString *tempLineEndString = [NSMutableString stringWithString:lineEndString]; [tempLineEndString replaceOccurrencesOfString:@"\\t" withString:@"\t" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; [tempLineEndString replaceOccurrencesOfString:@"\\n" withString:@"\n" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; [tempLineEndString replaceOccurrencesOfString:@"\\r" withString:@"\r" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; lineEndString = [NSString stringWithString:tempLineEndString]; // Updating the progress bar can take >20% of processing time - store details to only update when required @@ -967,13 +974,13 @@ lastProgressValue = 0; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; - + if ( !silently ) { - + // Set the progress text [singleProgressText setStringValue:NSLocalizedString(@"Exporting...", @"text showing that app is exporting to text file")]; [singleProgressText displayIfNeeded]; - + // Open progress sheet [NSApp beginSheet:singleProgressSheet @@ -986,7 +993,7 @@ escapedFieldSeparatorString = [NSString stringWithFormat:@"%@%@", escapeString, fieldSeparatorString]; escapedEnclosingString = [NSString stringWithFormat:@"%@%@", escapeString, enclosingString]; escapedLineEndString = [NSString stringWithFormat:@"%@%@", escapeString, lineEndString]; - + // Determine the total number of rows and starting row depending on supplied data format if (array == nil) { startingRow = outputFieldNames ? -1 : 0; @@ -998,17 +1005,17 @@ // Walk through the supplied data constructing the CSV string for ( i = startingRow ; i < totalRows ; i++ ) { - + // Update the progress bar [singleProgressBar setDoubleValue:((i+1)*100/totalRows)]; if ((int)[singleProgressBar doubleValue] > lastProgressValue) { lastProgressValue = (int)[singleProgressBar doubleValue]; [singleProgressBar displayIfNeeded]; } - + // Retrieve the row from the supplied data if (array == nil) { - + // Header row if (i == -1) { [csvRow setArray:[queryResult fetchFieldNames]]; @@ -1018,16 +1025,17 @@ } else { [csvRow setArray:[array objectAtIndex:i]]; } - + [csvString setString:@""]; for ( j = 0 ; j < [csvRow count] ; j++ ) { - - // For NULL objects supplied from a queryResult, no data is added to the cell + + // For NULL objects supplied from a queryResult, add an unenclosed null string as per prefs if ([[csvRow objectAtIndex:j] isKindOfClass:[NSNull class]]) { - [csvString appendString:fieldSeparatorString]; + [csvString appendString:nullString]; + if (j < [csvRow count] - 1) [csvString appendString:fieldSeparatorString]; continue; } - + // Retrieve the contents of this cell if ([[csvRow objectAtIndex:j] isKindOfClass:[NSData class]]) { dataConversionString = [[NSString alloc] initWithData:[csvRow objectAtIndex:j] encoding:tableEncoding]; @@ -1036,15 +1044,16 @@ } else { [csvCell setString:[[csvRow objectAtIndex:j] description]]; } - - // For NULL values supplied via an array no cell needs to be written. + + // For NULL values supplied via an array add the unenclosed null string as set in preferences if ( [csvCell isEqualToString:nullString] ) { + [csvString appendString:nullString]; // Add empty strings as a pair of enclosing characters. } else if ( [csvCell length] == 0 ) { [csvString appendString:enclosingString]; [csvString appendString:enclosingString]; - + } else { // Test whether this cell contains a number @@ -1057,31 +1066,33 @@ // Escape any occurrences of the escaping character [csvCell replaceOccurrencesOfString:escapeString - withString:escapedEscapeString - options:NSLiteralSearch - range:NSMakeRange(0,[csvCell length])]; - + withString:escapedEscapeString + options:NSLiteralSearch + range:NSMakeRange(0,[csvCell length])]; + // Escape any occurrences of the enclosure string if ( ![escapeString isEqualToString:enclosingString] ) { [csvCell replaceOccurrencesOfString:enclosingString - withString:escapedEnclosingString - options:NSLiteralSearch - range:NSMakeRange(0,[csvCell length])]; + withString:escapedEnclosingString + options:NSLiteralSearch + range:NSMakeRange(0,[csvCell length])]; } + + // Escape occurrences of the line end character + [csvCell replaceOccurrencesOfString:lineEndString + withString:escapedLineEndString + options:NSLiteralSearch + range:NSMakeRange(0,[csvCell length])]; // If the string isn't quoted or otherwise enclosed, escape occurrences of the - // field separators and the line ending separator. + // field separators if ( quoteFieldSeparators || csvCellIsNumeric ) { [csvCell replaceOccurrencesOfString:fieldSeparatorString - withString:escapedFieldSeparatorString - options:NSLiteralSearch - range:NSMakeRange(0,[csvCell length])]; - [csvCell replaceOccurrencesOfString:lineEndString - withString:escapedLineEndString - options:NSLiteralSearch - range:NSMakeRange(0,[csvCell length])]; + withString:escapedFieldSeparatorString + options:NSLiteralSearch + range:NSMakeRange(0,[csvCell length])]; } - + // Write out the cell data by appending strings - this is significantly faster than stringWithFormat. if (csvCellIsNumeric) { [csvString appendString:csvCell]; @@ -1091,12 +1102,12 @@ [csvString appendString:enclosingString]; } } - [csvString appendString:fieldSeparatorString]; + if (j < [csvRow count] - 1) [csvString appendString:fieldSeparatorString]; } - + // Append the line ending to the string for this row [csvString appendString:lineEndString]; - + // Write it to the fileHandle [fileHandle writeData:[csvString dataUsingEncoding:tableEncoding]]; } @@ -1127,7 +1138,7 @@ BOOL isEscaped, br; int fieldCount = nil; int x,i,j; - + //repare tabs and line ends tempTerminated = [NSMutableString stringWithString:terminated]; [tempTerminated replaceOccurrencesOfString:@"\\t" withString:@"\t" @@ -1185,7 +1196,10 @@ } } - //add line to array + // Skip blank lines + if (![tempString length]) continue; + + // Add the line to the array [linesArray addObject:[NSString stringWithString:tempString]]; } @@ -1198,13 +1212,14 @@ fieldCount = [tempRowArray count]; } else { while ( [tempRowArray count] < fieldCount ) { - [tempRowArray addObject:@"NULL"]; + [tempRowArray addObject:[NSString stringWithString:[prefs objectForKey:@"nullValue"]]]; } } for ( i = 0 ; i < [tempRowArray count] ; i++ ) { - if ( [[tempRowArray objectAtIndex:i] isEqualToString:@"NULL"] || [[tempRowArray objectAtIndex:i] isEqualToString:@""] || [[tempRowArray objectAtIndex:i] isEqualToString:@"\\N"] || [[tempRowArray objectAtIndex:i] isEqualToString:[prefs objectForKey:@"nullValue"]] ) { - - //put nsnull object to array if field contains un-enclosed NULL string + + // Insert a NSNull object if the cell contains an unescaped null character or an unescaped string + // which matches the NULL string set in preferences. + if ( [[tempRowArray objectAtIndex:i] isEqualToString:@"\\N"] || [[tempRowArray objectAtIndex:i] isEqualToString:[prefs objectForKey:@"nullValue"]] ) { [tempRowArray replaceObjectAtIndex:i withObject:[NSNull null]]; } else { @@ -1221,6 +1236,11 @@ [mutableField deleteCharactersInRange:NSMakeRange(([mutableField length]-[enclosed length]),[enclosed length])]; } } + if ( [mutableField length] >= [enclosed length] ) { + if ( [[mutableField substringFromIndex:([mutableField length]-[enclosed length])] isEqualToString:enclosed] ) { + [mutableField deleteCharactersInRange:NSMakeRange(([mutableField length]-[enclosed length]),[enclosed length])]; + } + } //strip escaped characters if ( ![enclosed isEqualToString:@""] ) { [mutableField replaceOccurrencesOfString:[NSString stringWithFormat:@"%@%@", escaped, enclosed] withString:enclosed options:NSLiteralSearch range:NSMakeRange(0, [mutableField length])]; @@ -1259,13 +1279,13 @@ int i,j, startingRow, totalRows, progressBarWidth, lastProgressValue; if (queryResult != nil && [queryResult numOfRows]) [queryResult dataSeek:0]; - + // Updating the progress bar can take >20% of processing time - store details to only update when required progressBarWidth = (int)[singleProgressBar bounds].size.width; lastProgressValue = 0; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; - + // Set up an array of encoded field names as opening and closing tags if (array == nil) { [xmlRow setArray:[queryResult fetchFieldNames]]; @@ -1275,17 +1295,17 @@ for ( j = 0; j < [xmlRow count]; j++ ) { [xmlTags addObject:[NSMutableArray array]]; [[xmlTags objectAtIndex:j] addObject:[NSString stringWithFormat:@"\t\t<%@>", - [self htmlEscapeString:[[xmlRow objectAtIndex:j] description]]]]; + [self htmlEscapeString:[[xmlRow objectAtIndex:j] description]]]]; [[xmlTags objectAtIndex:j] addObject:[NSString stringWithFormat:@"</%@>\n", - [self htmlEscapeString:[[xmlRow objectAtIndex:j] description]]]]; + [self htmlEscapeString:[[xmlRow objectAtIndex:j] description]]]]; } if ( !silently ) { - + // Set the progress text [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")]; [singleProgressText displayIfNeeded]; - + // Open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self @@ -1298,21 +1318,21 @@ [xmlString appendString:@"<!--\n-\n"]; [xmlString appendString:@"- Sequel Pro dump\n"]; [xmlString appendString:[NSString stringWithFormat:@"- Version %@\n", - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [xmlString appendString:@"- http://code.google.com/p/sequel-pro\n-\n"]; [xmlString appendString:[NSString stringWithFormat:@"- Host: %@ (MySQL %@)\n", - [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; + [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [xmlString appendString:[NSString stringWithFormat:@"- Database: %@\n", [tableDocumentInstance database]]]; [xmlString appendString:[NSString stringWithFormat:@"- Generation Time: %@\n", [NSDate date]]]; [xmlString appendString:@"-\n-->\n\n"]; [fileHandle writeData:[xmlString dataUsingEncoding:tableEncoding]]; } - + // Write an opening tag in the form of the table name [fileHandle writeData:[[NSString stringWithFormat:@"\t<%@>\n", - [self htmlEscapeString:table]] - dataUsingEncoding:tableEncoding]]; - + [self htmlEscapeString:table]] + dataUsingEncoding:tableEncoding]]; + // Determine the total number of rows and starting row depending on supplied data format if (array == nil) { startingRow = 0; @@ -1321,28 +1341,28 @@ startingRow = 1; totalRows = [array count]; } - + // Walk through the array, contructing the XML string. for ( i = 1 ; i < totalRows ; i++ ) { - + // Update the progress bar [singleProgressBar setDoubleValue:((i+1)*100/totalRows)]; if ((int)[singleProgressBar doubleValue] > lastProgressValue) { lastProgressValue = (int)[singleProgressBar doubleValue]; [singleProgressBar displayIfNeeded]; } - + // Retrieve the row from the supplied data if (array == nil) { [xmlRow setArray:[queryResult fetchRowAsArray]]; } else { [xmlRow setArray:[array objectAtIndex:i]]; } - + // Construct the row [xmlString setString:@"\t<row>\n"]; for ( j = 0 ; j < [xmlRow count] ; j++ ) { - + // Retrieve the contents of this tag if ([[xmlRow objectAtIndex:j] isKindOfClass:[NSData class]]) { dataConversionString = [[NSString alloc] initWithData:[xmlRow objectAtIndex:j] encoding:tableEncoding]; @@ -1351,7 +1371,7 @@ } else { [xmlItem setString:[[xmlRow objectAtIndex:j] description]]; } - + // Add the opening and closing tag and the contents to the XML string [xmlString appendString:[[xmlTags objectAtIndex:j] objectAtIndex:0]]; [xmlString appendString:[self htmlEscapeString:xmlItem]]; @@ -1362,12 +1382,12 @@ // Write the row to the filehandle [fileHandle writeData:[xmlString dataUsingEncoding:tableEncoding]]; } - + // Write the closing tag for the table [fileHandle writeData:[[NSString stringWithFormat:@"\t</%@>", - [self htmlEscapeString:table]] - dataUsingEncoding:tableEncoding]]; - + [self htmlEscapeString:table]] + dataUsingEncoding:tableEncoding]]; + // Close the progress sheet if appropriate if ( !silently ) { [NSApp endSheet:singleProgressSheet]; @@ -1385,14 +1405,14 @@ { int i; NSMutableArray *selectedTables = [NSMutableArray array]; - + // Extract the table names of the selected tables for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { [selectedTables addObject:[NSString stringWithString:[[tables objectAtIndex:i] objectAtIndex:1]]]; } } - + return [self exportTables:selectedTables toFileHandle:fileHandle usingFormat:type]; } @@ -1408,7 +1428,8 @@ NSMutableString *infoString = [NSMutableString string]; NSMutableString *errors = [NSMutableString string]; NSStringEncoding connectionEncoding = [mySQLConnection encoding]; - + NSMutableString *csvLineEnd; + // Reset the interface [errorsView setString:@""]; [errorsView displayIfNeeded]; @@ -1416,37 +1437,49 @@ [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; - + // Open the progress sheet [NSApp beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self - didEndSelector:nil contextInfo:nil]; - - - // Add the dump header to the dump file, dependant on export type. + modalForWindow:tableWindow modalDelegate:self + didEndSelector:nil contextInfo:nil]; + + + // Add a dump header to the dump file, dependant on export type. if ( [type isEqualToString:@"csv"] ) { - [infoString setString:[NSString stringWithFormat:@"Host: %@ Database: %@ Generation Time: %@\n\n", - [tableDocumentInstance host], [tableDocumentInstance database], [NSDate date]]]; + csvLineEnd = [NSMutableString stringWithString:[exportMultipleLinesTerminatedField stringValue]]; + [csvLineEnd replaceOccurrencesOfString:@"\\t" withString:@"\t" + options:NSLiteralSearch + range:NSMakeRange(0, [csvLineEnd length])]; + [csvLineEnd replaceOccurrencesOfString:@"\\n" withString:@"\n" + options:NSLiteralSearch + range:NSMakeRange(0, [csvLineEnd length])]; + [csvLineEnd replaceOccurrencesOfString:@"\\r" withString:@"\r" + options:NSLiteralSearch + range:NSMakeRange(0, [csvLineEnd length])]; + if ([selectedTables count] > 1) { + [infoString setString:[NSString stringWithFormat:@"Host: %@ Database: %@ Generation Time: %@%@%@", + [tableDocumentInstance host], [tableDocumentInstance database], [NSDate date], csvLineEnd, csvLineEnd]]; + } } else if ( [type isEqualToString:@"xml"] ) { [infoString setString:@"<?xml version=\"1.0\"?>\n\n"]; [infoString appendString:@"<!--\n-\n"]; [infoString appendString:@"- Sequel Pro dump\n"]; [infoString appendString:[NSString stringWithFormat:@"- Version %@\n", - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [infoString appendString:@"- http://code.google.com/p/sequel-pro\n-\n"]; [infoString appendString:[NSString stringWithFormat:@"- Host: %@ (MySQL %@)\n", - [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; + [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [infoString appendString:[NSString stringWithFormat:@"- Database: %@\n", [tableDocumentInstance database]]]; [infoString appendString:[NSString stringWithFormat:@"- Generation Time: %@\n", [NSDate date]]]; [infoString appendString:@"-\n-->\n\n\n"]; [infoString appendString:[NSString stringWithFormat:@"<%@>\n\n\n", - [self htmlEscapeString:[tableDocumentInstance database]]]]; + [self htmlEscapeString:[tableDocumentInstance database]]]]; } [fileHandle writeData:[infoString dataUsingEncoding:connectionEncoding]]; - + // Loop through the selected tables for ( i = 0 ; i < [selectedTables count] ; i++ ) { - + // Update the progress text and reset the progress bar to indeterminate status tableName = [selectedTables objectAtIndex:i]; [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %i of %i (%@): fetching data...", @"text showing that app is fetching data for table dump"), (i+1), [selectedTables count], tableName]]; @@ -1454,20 +1487,20 @@ [singleProgressBar setIndeterminate:YES]; [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar startAnimation:self]; - - // For CSV exports, output the name of the table - if ( [type isEqualToString:@"csv"] ) { - [fileHandle writeData:[[NSString stringWithFormat:@"Table %@\n\n", tableName] dataUsingEncoding:connectionEncoding]]; + + // For CSV exports of more than one table, output the name of the table + if ( [type isEqualToString:@"csv"] && [selectedTables count] > 1) { + [fileHandle writeData:[[NSString stringWithFormat:@"Table %@%@%@", tableName, csvLineEnd, csvLineEnd] dataUsingEncoding:connectionEncoding]]; } - + // Retrieve all the content within this table queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; - + // Note any errors during retrieval if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; } - + // Update the progress text and set the progress bar back to determinate [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %i of %i (%@): Writing...", @"text showing that app is writing data for table export"), (i+1), [selectedTables count], tableName]]; [singleProgressText displayIfNeeded]; @@ -1476,36 +1509,40 @@ [singleProgressBar setIndeterminate:NO]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; - + // Use the appropriate export method to write the data to file if ( [type isEqualToString:@"csv"] ) { [self writeCsvForArray:nil orQueryResult:queryResult - toFileHandle:fileHandle - outputFieldNames:[exportMultipleFieldNamesSwitch state] - terminatedBy:[exportMultipleFieldsTerminatedField stringValue] - enclosedBy:[exportMultipleFieldsEnclosedField stringValue] - escapedBy:[exportMultipleFieldsEscapedField stringValue] - lineEnds:[exportMultipleLinesTerminatedField stringValue] - silently:YES]; + toFileHandle:fileHandle + outputFieldNames:[exportMultipleFieldNamesSwitch state] + terminatedBy:[exportMultipleFieldsTerminatedField stringValue] + enclosedBy:[exportMultipleFieldsEnclosedField stringValue] + escapedBy:[exportMultipleFieldsEscapedField stringValue] + lineEnds:[exportMultipleLinesTerminatedField stringValue] + silently:YES]; + + // Add a spacer to the file + [fileHandle writeData:[[NSString stringWithFormat:@"%@%@%@", csvLineEnd, csvLineEnd, csvLineEnd] dataUsingEncoding:connectionEncoding]]; } else if ( [type isEqualToString:@"xml"] ) { [self writeXmlForArray:nil orQueryResult:queryResult - toFileHandle:fileHandle - tableName:tableName - withHeader:NO - silently:YES]; + toFileHandle:fileHandle + tableName:tableName + withHeader:NO + silently:YES]; + + // Add a spacer to the file + [fileHandle writeData:[[NSString stringWithString:@"\n\n\n"] dataUsingEncoding:connectionEncoding]]; } - // Add a spacer to the file - [fileHandle writeData:[[NSString stringWithString:@"\n\n\n"] dataUsingEncoding:connectionEncoding]]; } - + // For XML output, close the database tag if ( [type isEqualToString:@"xml"] ) { [fileHandle writeData:[[NSString stringWithFormat:@"</%@>", [self htmlEscapeString:[tableDocumentInstance database]]] - dataUsingEncoding:connectionEncoding]]; + dataUsingEncoding:connectionEncoding]]; } - + // Close the progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; @@ -1656,13 +1693,13 @@ // For the backtick character treat the string as ended if ( ([queries characterAtIndex:i] == '`') && (stringType == '`') ) { - + inString = NO; break; - - // Otherwise, prepare to treat the string as ended after a stringType.... + + // Otherwise, prepare to treat the string as ended after a stringType.... } else if ( [queries characterAtIndex:i] == stringType ) { - + // ...but only if the stringType isn't escaped with an *odd* number of escaping characters. escaped = NO; j = 1; @@ -1796,10 +1833,9 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn if ([[[aTableColumn dataCell] class] isEqualTo:[NSPopUpButtonCell class]]) { [(NSPopUpButtonCell *)[aTableColumn dataCell] removeAllItems]; [(NSPopUpButtonCell *)[aTableColumn dataCell] addItemWithTitle:NSLocalizedString(@"Do not import", @"text for csv import drop downs")]; - [(NSPopUpButtonCell *)[aTableColumn dataCell] addItemsWithTitles:[importArray objectAtIndex:currentRow]]; - //[(NSPopUpButtonCell *)[aTableColumn dataCell] selectItemAtIndex:[fieldMappingArray objectAtIndex:rowIndex]]; + [(NSPopUpButtonCell *)[aTableColumn dataCell] addItemsWithTitles:fieldMappingButtonOptions]; } - + return [fieldMappingArray objectAtIndex:rowIndex]; } } else { @@ -1832,6 +1868,7 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn self = [super init]; tables = [[NSMutableArray alloc] init]; + fieldMappingButtonOptions = [[NSMutableArray alloc] init]; return self; } @@ -1842,6 +1879,7 @@ objectValueForTableColumn:(NSTableColumn *)aTableColumn [tables release]; [importArray release]; + [fieldMappingButtonOptions release]; [fieldMappingArray release]; [savePath release]; [openPath release]; diff --git a/TablesList.h b/TablesList.h index d7d37d2e..7270219b 100644 --- a/TablesList.h +++ b/TablesList.h @@ -92,8 +92,4 @@ - (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView; - (void)tableViewSelectionDidChange:(NSNotification *)aNotification; -//last but not least -- (id)init; -- (void)dealloc; - @end diff --git a/TablesList.m b/TablesList.m index 500ac349..40ea87c5 100644 --- a/TablesList.m +++ b/TablesList.m @@ -543,12 +543,12 @@ traps enter and esc and edit/cancel without entering next row } } -/* -loads a table in content or source view (if tab selected) -*/ +/** + * Loads a table in content or source view (if tab selected) + */ - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { - if ( [tablesListView numberOfSelectedRows] == 1 ) { + if ( [tablesListView numberOfSelectedRows] == 1 ) { if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == 0 ) { [tableSourceInstance loadTable:[tables objectAtIndex:[tablesListView selectedRow]]]; structureLoaded = YES; @@ -575,6 +575,10 @@ loads a table in content or source view (if tab selected) [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance user], [tableDocumentInstance host], [tableDocumentInstance database], [tables objectAtIndex:[tablesListView selectedRow]]]]; + // Update connection characater set encoding based on the table's encoding if required + if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"encoding"] isEqualToString:@"Autodetect"]) { + [tableDocumentInstance detectTableEncodingForTable:[tables objectAtIndex:[tablesListView selectedRow]]]; + } } else { [tableSourceInstance loadTable:nil]; [tableContentInstance loadTable:nil]; @@ -589,8 +593,6 @@ loads a table in content or source view (if tab selected) } } - - - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex { return (rowIndex != 0); |