diff options
-rw-r--r-- | Source/TableDump.h | 1 | ||||
-rw-r--r-- | Source/TableDump.m | 160 |
2 files changed, 114 insertions, 47 deletions
diff --git a/Source/TableDump.h b/Source/TableDump.h index a9831c41..d0f4d336 100644 --- a/Source/TableDump.h +++ b/Source/TableDump.h @@ -118,6 +118,7 @@ - (void)export; - (void)exportFile:(int)tag; - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo; +- (void)exportBackgroundProcess:(NSDictionary *)exportAction; // Import methods - (void)importFile; diff --git a/Source/TableDump.m b/Source/TableDump.m index 90c27a42..222612b5 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -235,31 +235,54 @@ didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } -/* - Save the export file; open a file handle, pass it to the appropriate data-writing function for streaming the export, and close the handle. +/** + * When the export "Save" dialog is closed, fire up a background thread to perform + * the requested export. */ - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo { - NSFileHandle *fileHandle = nil; - BOOL success; - [sheet orderOut:self]; if ( returnCode != NSOKButton ) return; + // Save path to preferences + [prefs setObject:[sheet directory] forKey:@"savePath"]; + + // Set up the details required for the export and pass them into a new worker thread + NSDictionary *exportProcessDictionary = [NSDictionary dictionaryWithObjectsAndKeys: + contextInfo, @"action", + [sheet filename], @"filename", + nil]; + [NSThread detachNewThreadSelector:@selector(exportBackgroundProcess:) toTarget:self withObject:exportProcessDictionary]; +} + +/** + * Save the export file in a background thread; open a file handle, pass it in to + * the appropriate data-writing function for streaming the export data to, and + * close the handle. + */ +- (void)exportBackgroundProcess:(NSDictionary *)exportAction +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *exportActionName = [exportAction objectForKey:@"action"]; + NSString *exportFile = [exportAction objectForKey:@"filename"]; + NSFileHandle *fileHandle = nil; + BOOL success; + // Start the notification timer to allow notifications to be shown even if frontmost for long queries [[SPGrowlController sharedGrowlController] setVisibilityForNotificationName:@"Export Finished"]; - // Save path to preferences - [prefs setObject:[sheet directory] forKey:@"savePath"]; + // Reset the progress cancelled boolean + progressCancelled = NO; // 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]] - || !(fileHandle = [NSFileHandle fileHandleForWritingAtPath:[sheet filename]]) ) { + if ( [[NSFileManager defaultManager] fileExistsAtPath:exportFile] ) { + if ( ![[NSFileManager defaultManager] isWritableFileAtPath:exportFile] + || !(fileHandle = [NSFileHandle fileHandleForWritingAtPath:exportFile]) ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't replace the file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be replaced")); + [pool release]; return; } @@ -268,37 +291,39 @@ // Otherwise attempt to create a file } else { - if ( ![[NSFileManager defaultManager] createFileAtPath:[sheet filename] contents:[NSData data] attributes:nil] ) { + if ( ![[NSFileManager defaultManager] createFileAtPath:exportFile 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")); + [pool release]; return; } // Retrieve a filehandle for the file, attempting to delete it on failure. - fileHandle = [NSFileHandle fileHandleForWritingAtPath:[sheet filename]]; + fileHandle = [NSFileHandle fileHandleForWritingAtPath:exportFile]; if ( !fileHandle ) { - [[NSFileManager defaultManager] removeFileAtPath:[sheet filename] handler:nil]; + [[NSFileManager defaultManager] removeFileAtPath:exportFile 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")); + [pool release]; return; } } // Export the tables selected in the MySQL export sheet to a file - if ( [contextInfo isEqualToString:@"exportDump"] ) { + if ( [exportActionName isEqualToString:@"exportDump"] ) { success = [self dumpSelectedTablesAsSqlToFileHandle:fileHandle]; // Export the full resultset for the currently selected table to a file in CSV format - } else if ( [contextInfo isEqualToString:@"exportTableContentAsCSV"] ) { + } else if ( [exportActionName isEqualToString:@"exportTableContentAsCSV"] ) { success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"csv" usingMulti:NO]; // Export the full resultset for the currently selected table to a file in XML format - } else if ( [contextInfo isEqualToString:@"exportTableContentAsXML"] ) { + } else if ( [exportActionName isEqualToString:@"exportTableContentAsXML"] ) { success = [self exportTables:[NSArray arrayWithObject:[tableDocumentInstance table]] toFileHandle:fileHandle usingFormat:@"xml" usingMulti:NO]; // Export the current "browse" view to a file in CSV or XML format - } else if ( [contextInfo isEqualToString:@"exportBrowseViewAsCSV"] - || [contextInfo isEqualToString:@"exportBrowseViewAsXML"] ) + } else if ( [exportActionName isEqualToString:@"exportBrowseViewAsCSV"] + || [exportActionName isEqualToString:@"exportBrowseViewAsXML"] ) { // Start an indeterminate progress sheet, as getting the current result set can take a significant period of time @@ -307,11 +332,12 @@ [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar setIndeterminate:YES]; [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; [singleProgressBar startAnimation:self]; NSArray *contentViewArray = [tableContentInstance currentResult]; - if ( [contextInfo isEqualToString:@"exportBrowseViewAsCSV"] ) { + if ( [exportActionName isEqualToString:@"exportBrowseViewAsCSV"] ) { success = [self writeCsvForArray:contentViewArray orStreamingResult:nil toFileHandle:fileHandle outputFieldNames:[exportFieldNamesSwitch state] @@ -335,21 +361,26 @@ [self closeAndStopProgressSheet]; // Export the current custom query result set to a file in CSV or XML format - } else if ( [contextInfo isEqualToString:@"exportCustomResultAsCSV"] - || [contextInfo isEqualToString:@"exportCustomResultAsXML"] ) + } else if ( [exportActionName isEqualToString:@"exportCustomResultAsCSV"] + || [exportActionName isEqualToString:@"exportCustomResultAsXML"] ) { // Start an indeterminate progress sheet, as getting the current result set can take a significant period of time - [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to CSV", @"title showing that application is saving custom query view as CSV")]]; + if ([exportActionName isEqualToString:@"exportCustomResultAsCSV"]) { + [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to CSV", @"title showing that application is saving custom query view as CSV")]]; + } else { + [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to XML", @"title showing that application is saving custom query view as XML")]]; + } [singleProgressText setStringValue:NSLocalizedString(@"Exporting data...", @"text showing that app is preparing data")]; [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar setIndeterminate:YES]; [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; [singleProgressBar startAnimation:self]; NSArray *customQueryViewArray = [customQueryInstance currentResult]; - if ( [contextInfo isEqualToString:@"exportCustomResultAsCSV"] ) { + if ( [exportActionName isEqualToString:@"exportCustomResultAsCSV"] ) { success = [self writeCsvForArray:customQueryViewArray orStreamingResult:nil toFileHandle:fileHandle outputFieldNames:[exportFieldNamesSwitch state] @@ -373,39 +404,44 @@ [self closeAndStopProgressSheet]; // Export multiple tables to a file in CSV format - } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsCSV"] ) { + } else if ( [exportActionName isEqualToString:@"exportMultipleTablesAsCSV"] ) { success = [self exportSelectedTablesToFileHandle:fileHandle usingFormat:@"csv"]; // Export multiple tables to a file in XML format - } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsXML"] ) { + } else if ( [exportActionName isEqualToString:@"exportMultipleTablesAsXML"] ) { success = [self exportSelectedTablesToFileHandle:fileHandle usingFormat:@"xml"]; // Export the tables selected in the MySQL export sheet to a file - } else if ( [contextInfo isEqualToString:@"exportDot"] ) { + } else if ( [exportActionName isEqualToString:@"exportDot"] ) { success = [self dumpSchemaAsDotToFileHandle:fileHandle]; // Unknown operation } else { - ALog(@"Unknown export operation: %@", [contextInfo description]); + ALog(@"Unknown export operation: %@", [exportActionName description]); + [pool release]; return; } // Close the file handle [fileHandle closeFile]; - - if ( !success ) { + + // If progress was cancelled, remove the file + if (progressCancelled) { + [[NSFileManager defaultManager] removeItemAtPath:exportFile error:nil]; + } + + // Display error message on problems + if ( !progressCancelled && !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")); } - if (progressCancelled) - { - progressCancelled = NO; - } + // Export finished Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Export Finished" - description:[NSString stringWithFormat:NSLocalizedString(@"Finished exporting to %@",@"description for finished exporting growl notification"), [[sheet filename] lastPathComponent]] + description:[NSString stringWithFormat:NSLocalizedString(@"Finished exporting to %@",@"description for finished exporting growl notification"), [exportFile lastPathComponent]] window:tableWindow notificationName:@"Export Finished"]; + [pool release]; } #pragma mark - @@ -1310,6 +1346,7 @@ [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; @@ -1360,6 +1397,7 @@ // Loop through the selected tables for ( i = 0 ; i < [selectedTables count] ; i++ ) { + if (progressCancelled) break; lastProgressValue = 0; // Update the progress text and reset the progress bar to indeterminate status while fetching data @@ -1461,6 +1499,11 @@ j = 0; exportAutoReleasePool = [[NSAutoreleasePool alloc] init]; while (theRow = [streamingResult fetchNextRowAsArray]) { + if (progressCancelled) { + [mySQLConnection cancelCurrentQuery]; + [streamingResult cancelResultLoad]; + break; + } j++; [sqlString setString:@""]; @@ -1533,7 +1576,7 @@ // Write this row to the file [fileHandle writeData:[sqlString dataUsingEncoding:NSUTF8StringEncoding]]; } - + // Complete the command [fileHandle writeData:[[NSString stringWithString:@";\n\n"] dataUsingEncoding:NSUTF8StringEncoding]]; @@ -1542,7 +1585,7 @@ [metaString appendString:[NSString stringWithFormat:@"/*!40000 ALTER TABLE %@ ENABLE KEYS */;\n", [tableName backtickQuotedString]]]; [metaString appendString:@"UNLOCK TABLES;\n"]; [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; - + if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; if ( [addErrorsSwitch state] == NSOnState ) { @@ -1558,11 +1601,11 @@ // Release the result set [streamingResult release]; } - + // Add an additional separator between tables [fileHandle writeData:[[NSString stringWithString:@"\n\n"] dataUsingEncoding:NSUTF8StringEncoding]]; } - + // Process any deferred views, adding commands to delete the placeholder tables and add the actual views viewSyntaxEnumerator = [viewSyntaxes keyEnumerator]; while (tableName = [viewSyntaxEnumerator nextObject]) { @@ -1593,7 +1636,7 @@ setConnectionEncoding:[NSString stringWithFormat:@"%@%@", previousConnectionEncoding, previousConnectionEncodingViaLatin1?@"-":@""] reloadingViews:NO]; [previousConnectionEncoding release]; - + // Close the progress sheet [self closeAndStopProgressSheet]; @@ -1638,6 +1681,7 @@ [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; [metaString setString:@"// Generated by: Sequel Pro\n"]; [metaString appendString:[NSString stringWithFormat:@"// Version %@\n", @@ -1670,6 +1714,7 @@ // tables here for ( int i = 0 ; i < [tables count] ; i++ ) { + if (progressCancelled) break; NSString *tableName = [[tables objectAtIndex:i] objectAtIndex:1]; NSDictionary *tinfo = [tableDataInstance informationForTable:tableName]; @@ -1705,15 +1750,18 @@ // see about relations cinfo = [tinfo objectForKey:@"constraints"]; for( int j = 0; j < [cinfo count]; j++ ) { - // get the column refs. these can be comma separated. - NSString *ccol = [NSArrayObjectAtIndex(cinfo, j) objectForKey:@"columns"]; + if (progressCancelled) break; + + // Get the column references. Currently the columns themselves are an array, + // while reference columns and tables are comma separated if there are more than + // one. Only use the first of each for the time being. + NSArray *ccols = [NSArrayObjectAtIndex(cinfo, j) objectForKey:@"columns"]; + NSString *ccol = NSArrayObjectAtIndex(ccols, 0); NSString *rcol = [NSArrayObjectAtIndex(cinfo, j) objectForKey:@"ref_columns"]; NSString *extra = @""; - NSArray *tc = [ccol componentsSeparatedByString:@","]; - if( [tc count] > 1 ) { + if( [ccols count] > 1 ) { extra = @" [ arrowhead=crow, arrowtail=odiamond ]"; - ccol = NSArrayObjectAtIndex(tc, 0); - rcol = NSArrayObjectAtIndex([ccol componentsSeparatedByString:@","], 0); + rcol = NSArrayObjectAtIndex([rcol componentsSeparatedByString:@","], 0); } [fkInfo addObject:[NSString stringWithFormat:@"%@:%@ -> %@:%@ %@", tableName, @@ -1842,6 +1890,7 @@ [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; } // Set up escaped versions of strings for substitution within the loop @@ -1861,6 +1910,13 @@ csvExportPool = [[NSAutoreleasePool alloc] init]; currentPoolDataLength = 0; while (1) { + if (progressCancelled) { + if (streamingResult) { + [mySQLConnection cancelCurrentQuery]; + [streamingResult cancelResultLoad]; + } + break; + } // Retrieve the next row from the supplied data, either directly from the array... if (array) { @@ -2037,6 +2093,7 @@ // 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 setIndeterminate:NO]; [singleProgressBar setMaxValue:progressBarWidth]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; @@ -2065,6 +2122,7 @@ [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [singleProgressSheet makeKeyWindow]; } // Output the XML header if required @@ -2097,6 +2155,13 @@ xmlExportPool = [[NSAutoreleasePool alloc] init]; currentPoolDataLength = 0; while (1) { + if (progressCancelled) { + if (streamingResult) { + [mySQLConnection cancelCurrentQuery]; + [streamingResult cancelResultLoad]; + } + break; + } // Retrieve the next row from the supplied data, either directly from the array... if (array) { @@ -2229,8 +2294,8 @@ [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; - - + [singleProgressSheet makeKeyWindow]; + // Add a dump header to the dump file, dependant on export type. if ( [type isEqualToString:@"csv"] ) { csvLineEnd = [NSMutableString stringWithString:[exportMultipleLinesTerminatedField stringValue]]; @@ -2266,6 +2331,7 @@ // Loop through the selected tables for ( i = 0 ; i < [selectedTables count] && !progressCancelled; i++ ) { + if (progressCancelled) break; // Update the progress text and reset the progress bar to indeterminate status tableName = [selectedTables objectAtIndex:i]; |