Permalink
Browse files

#2658: Perform duplicate database operation on a background thread an…

…d display the indeterminate progress indicator.
  • Loading branch information...
stuconnolly committed Jun 17, 2018
1 parent 707dc02 commit dc2eec201ad849c675a03a794490b4e4e3117465
Showing with 92 additions and 102 deletions.
  1. +92 −102 Source/SPDatabaseDocument.m
@@ -73,10 +73,8 @@
#import "SPGotoDatabaseController.h"
#import "SPFunctions.h"
#import "SPCreateDatabaseInfo.h"
#ifndef SP_CODA /* headers */
#import "SPAppController.h"
#import "SPBundleHTMLOutputController.h"
#endif
#import "SPTableTriggers.h"
#import "SPTableStructure.h"
#import "SPPrintAccessory.h"
@@ -92,10 +90,13 @@
#include <libkern/OSAtomic.h>
// Constants
static NSString *SPCopyDatabaseAction = @"SPCopyDatabase";
static NSString *SPConfirmCopyDatabaseAction = @"SPConfirmCopyDatabase";
static NSString *SPRenameDatabaseAction = @"SPRenameDatabase";
static NSString *SPAlterDatabaseAction = @"SPAlterDatabase";
static NSString *SPSaveDocumentPreferences = @"SPSaveDocumentPreferences";
static NSString *SPNewDatabaseDetails = @"SPNewDatabaseDetails";
static NSString *SPNewDatabaseName = @"SPNewDatabaseName";
static NSString *SPNewDatabaseCopyContent = @"SPNewDatabaseCopyContent";
static int64_t SPDatabaseDocumentInstanceCounter = 0;
@@ -111,6 +112,7 @@ - (void)_processDatabaseChangedBundleTriggerActions;
- (void)_addPreferenceObservers;
- (void)_removePreferenceObservers;
#pragma mark - SPDatabaseViewControllerPrivateAPI
- (void)_loadTabTask:(NSNumber *)tabViewItemIndexNumber;
@@ -873,9 +875,8 @@ Next we need to ask the user to select another connection (from the favourites l
NSLog(@"=================");
}
#ifndef SP_CODA /* operations on whole databases */
/**
* opens the copy database sheet and copies the databsae
* Opens the copy database sheet and copies the databsae.
*/
- (IBAction)copyDatabase:(id)sender
{
@@ -901,13 +902,12 @@ - (IBAction)copyDatabase:(id)sender
[databaseCopyNameField setStringValue:selectedDatabase];
[copyDatabaseMessageField setStringValue:selectedDatabase];
[NSApp beginSheet:databaseCopySheet
modalForWindow:parentWindow
modalDelegate:self
didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo:SPCopyDatabaseAction];
[parentWindow beginSheet:databaseCopySheet completionHandler:^(NSInteger returnCode) {

This comment has been minimized.

@dmoagx

dmoagx Jun 19, 2018

Member

This breaks 10.6 compatibility, which we wanted to keep for the next release.

This comment has been minimized.

@stuconnolly

stuconnolly Jun 20, 2018

Member

Yep, my bad, will revert. When are we planning to ditch 10.6? It's rather old now :)

This comment has been minimized.

@dmoagx

dmoagx Jun 20, 2018

Member

Right after 1.2 AFAIR. We just kept it because the code still was compatible.

if (returnCode == NSOKButton) {
[self _copyDatabase];
}
}];
}
#endif
/**
* Opens the rename database sheet and renames the databsae.
@@ -1070,17 +1070,15 @@ - (NSArray *)allSystemDatabaseNames
*/
- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
{
#ifndef SP_CODA
// Those that are just setting a return code and don't need to order out the sheet. See SPAlertSheets+beginWaitingAlertSheetWithTitle:
if ([contextInfo isEqualToString:@"saveDocPrefSheetStatus"]) {
if ([contextInfo isEqualToString:SPSaveDocumentPreferences]) {
saveDocPrefSheetStatus = returnCode;
return;
}
else if ([contextInfo isEqualToString:SPConfirmCopyDatabaseAction]) {
confirmCopyDatabaseReturnCode = returnCode;
return;
}
#endif
// Order out current sheet to suppress overlapping of sheets
if ([sheet respondsToSelector:@selector(orderOut:)]) {
@@ -1095,17 +1093,6 @@ - (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSSt
if (returnCode == NSAlertDefaultReturn) {
[self _removeDatabase];
}
#ifdef SP_CODA
else {
// Reset chooseDatabaseButton
if ([[self database] length]) {
[chooseDatabaseButton selectItemWithTitle:[self database]];
}
else {
[chooseDatabaseButton selectItemAtIndex:0];
}
}
#endif
}
// Add a new database
else if ([contextInfo isEqualToString:@"addDatabase"]) {
@@ -1126,42 +1113,22 @@ - (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSSt
[chooseDatabaseButton selectItemAtIndex:0];
}
}
}
#ifndef SP_CODA
else if ([contextInfo isEqualToString:SPCopyDatabaseAction]) {
if (returnCode == NSOKButton) {
[self _copyDatabase];
}
}
#endif
else if ([contextInfo isEqualToString:SPRenameDatabaseAction]) {
if (returnCode == NSOKButton) {
[self _renameDatabase];
}
#ifdef SP_CODA
else {
// Reset chooseDatabaseButton
if ([[self database] length]) {
[chooseDatabaseButton selectItemWithTitle:[self database]];
}
else {
[chooseDatabaseButton selectItemAtIndex:0];
}
}
#endif
}
else if([contextInfo isEqualToString:SPAlterDatabaseAction]) {
[alterDatabaseCharsetHelper setEnabled:NO];
if(returnCode == NSOKButton) {
[self _alterDatabase];
}
}
#ifndef SP_CODA
// Close error status sheet for OPTIMIZE, CHECK, REPAIR etc.
else if ([contextInfo isEqualToString:@"statusError"]) {
if (statusValues) SPClear(statusValues);
}
#endif
}
#ifndef SP_CODA /* sheetDidEnd: */
@@ -3332,14 +3299,14 @@ - (void)saveConnectionPanelDidEnd:(NSSavePanel *)panel returnCode:(NSInteger)ret
- (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInBackground onlyPreferences:(BOOL)saveOnlyPreferences contextInfo:(NSDictionary*)contextInfo
{
// Do not save if no connection is/was available
if(saveInBackground && ([self mySQLVersion] == nil || ![[self mySQLVersion] length])) return NO;
if (saveInBackground && ([self mySQLVersion] == nil || ![[self mySQLVersion] length])) return NO;
NSMutableDictionary *spfDocData_temp = [NSMutableDictionary dictionary];
if(fileName == nil) fileName = [[self fileURL] path];
if (fileName == nil) fileName = [[self fileURL] path];
// Store save panel settings or take them from spfDocData
if(!saveInBackground && contextInfo == nil) {
if (!saveInBackground && contextInfo == nil) {
[spfDocData_temp setObject:[NSNumber numberWithBool:([saveConnectionEncrypt state]==NSOnState) ? YES : NO ] forKey:@"encrypted"];
if([[spfDocData_temp objectForKey:@"encrypted"] boolValue]) {
[spfDocData_temp setObject:[saveConnectionEncryptString stringValue] forKey:@"e_string"];
@@ -3351,16 +3318,17 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
if([[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string] length]) {
[spfDocData_temp setObject:[NSNumber numberWithBool:([saveConnectionIncludeQuery state] == NSOnState) ? YES : NO] forKey:@"save_editor_content"];
}
} else {
}
else {
// If contextInfo != nil call came from other SPDatabaseDocument while saving it as bundle
[spfDocData_temp addEntriesFromDictionary:(contextInfo == nil ? spfDocData : contextInfo)];
}
// Update only query favourites, history, etc. by reading the file again
if(saveOnlyPreferences) {
if (saveOnlyPreferences) {
// Check URL for safety reasons
if(![[[self fileURL] path] length] || [self isUntitled]) {
if (![[[self fileURL] path] length] || [self isUntitled]) {
NSLog(@"Couldn't save data. No file URL found!");
NSBeep();
return NO;
@@ -3372,13 +3340,13 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
NSData *pData = [NSData dataWithContentsOfFile:fileName options:NSUncachedRead error:&error];
if(pData && !error) {
if (pData && !error) {
NSDictionary *pDict = [NSPropertyListSerialization propertyListWithData:pData
options:NSPropertyListImmutable
format:NULL
error:&error];
if(pDict && !error) {
if (pDict && !error) {
[spf addEntriesFromDictionary:pDict];
}
}
@@ -3392,19 +3360,18 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
docWindow:parentWindow
modalDelegate:self
didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo:@"saveDocPrefSheetStatus"
contextInfo:SPSaveDocumentPreferences
infoText:[NSString stringWithFormat:NSLocalizedString(@"Connection data file “%@” couldn't be read. Please try to save the document under a different name.\n\nDetails: %@", @"message error while reading connection data file and suggesting to save it under a differnet name"), [fileName lastPathComponent], [error localizedDescription]]
returnCode:&saveDocPrefSheetStatus];
if(spf) [spf release];
if(saveDocPrefSheetStatus == NSAlertAlternateReturn) return YES;
return NO;
if (spf) [spf release];
return saveDocPrefSheetStatus == NSAlertAlternateReturn;
}
}
// For dispatching later
if(![[spf objectForKey:SPFFormatKey] isEqualToString:SPFConnectionContentType]) {
if (![[spf objectForKey:SPFFormatKey] isEqualToString:SPFConnectionContentType]) {
NSLog(@"SPF file format is not 'connection'.");
[spf release];
return NO;
@@ -3423,7 +3390,8 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
error:&error];
[spf release];
if(error) {
if (error) {
NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error while converting connection data", @"error while converting connection data")
defaultButton:NSLocalizedString(@"OK", @"OK button")
alternateButton:nil
@@ -3436,7 +3404,8 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
}
[plist writeToFile:fileName options:NSAtomicWrite error:&error];
if(error != nil) {
if (error != nil) {
NSAlert *errorAlert = [NSAlert alertWithError:error];
[errorAlert runModal];
return NO;
@@ -3488,6 +3457,7 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
// Determine whether to use encryption when adding the data
[spfStructure setObject:[spfDocData_temp objectForKey:@"encrypted"] forKey:@"encrypted"];
if (![[spfDocData_temp objectForKey:@"encrypted"] boolValue]) {
// Convert the content selection to encoded data
@@ -3502,7 +3472,8 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
}
[spfStructure setObject:spfData forKey:@"data"];
} else {
}
else {
NSMutableData *dataToEncrypt = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:dataToEncrypt] autorelease];
[archiver encodeObject:spfData forKey:@"data"];
@@ -3530,6 +3501,7 @@ - (BOOL)saveDocumentWithFilePath:(NSString *)fileName inBackground:(BOOL)saveInB
}
[plist writeToFile:fileName options:NSAtomicWrite error:&error];
if (error != nil){
NSAlert *errorAlert = [NSAlert alertWithError:error];
[errorAlert runModal];
@@ -6023,46 +5995,76 @@ - (void)setIsSavedInBundle:(BOOL)savedInBundle
#pragma mark -
#pragma mark Private API
#ifndef SP_CODA /* whole database operations */
/**
*
* This method *MUST* be called from the UI thread!

This comment has been minimized.

@dmoagx

dmoagx Jun 19, 2018

Member

This comment should be kept, because it is still true ([databaseCopyNameField stringValue] is a UI method)

This comment has been minimized.

@stuconnolly

stuconnolly Jun 20, 2018

Member

Yep, will add back.

* Copies the current database (and optionally it's content) on a separate thread.
*/
- (void)_copyDatabase
- (void)_copyDatabase
{
if ([[databaseCopyNameField stringValue] isEqualToString:@""]) {
NSString *newDatabaseName = [databaseCopyNameField stringValue];
if ([newDatabaseName isEqualToString:@""]) {
SPOnewayAlertSheet(NSLocalizedString(@"Error", @"error"), parentWindow, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given"));
return;
}
SPDatabaseCopy *dbActionCopy = [[SPDatabaseCopy alloc] init];
[dbActionCopy setConnection:[self getConnection]];
[dbActionCopy setMessageWindow:parentWindow];
BOOL copyWithContent = [copyDatabaseDataButton state] == NSOnState;
if ([dbActionCopy copyDatabaseFrom:[self createDatabaseInfo] to:[databaseCopyNameField stringValue] withContent:copyWithContent]) {
[self selectDatabase:[databaseCopyNameField stringValue] item:nil];
NSDictionary *databaseDetails = @{
SPNewDatabaseDetails : [self createDatabaseInfo],
SPNewDatabaseName : newDatabaseName,
SPNewDatabaseCopyContent : @([copyDatabaseDataButton state] == NSOnState)
};
[self startTaskWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Copying database '%@'...", @"Copying database task description"), [self database]]];
if ([NSThread isMainThread]) {
[NSThread detachNewThreadWithName:SPCtxt(@"SPDatabaseDocument copy database task", self)
target:self
selector:@selector(_copyDatabaseWithDetails:)
object:databaseDetails];;
}
else {
SPOnewayAlertSheet(
NSLocalizedString(@"Unable to copy database", @"unable to copy database message"),
parentWindow,
[NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to copy the database '%@' to '%@'.", @"unable to copy database message informative message"), [self database], [databaseCopyNameField stringValue]]
);
[self _copyDatabaseWithDetails:databaseDetails];
}
}
- (void)_copyDatabaseWithDetails:(NSDictionary *)databaseDetails
{
@autoreleasepool
{
SPDatabaseCopy *databaseCopy = [[SPDatabaseCopy alloc] init];
[databaseCopy setConnection:[self getConnection]];
NSString *newDatabaseName = databaseDetails[SPNewDatabaseName];
BOOL success = [databaseCopy copyDatabaseFrom:databaseDetails[SPNewDatabaseDetails]

This comment has been minimized.

@dmoagx

dmoagx Jun 19, 2018

Member

Personally, I feel we should not use the nsdict[key] syntax, because it mixes C and ObjC syntax. It's easy for the compiler to figure out which one it is, but SP has a lot of simple C arrays, so as a human it isn't really obvious which case is intended (same goes for the obj.property syntax and structs).

to:newDatabaseName
withContent:[databaseDetails[SPNewDatabaseCopyContent] boolValue]];
[databaseCopy release];
// Select newly created database
[[self onMainThread] selectDatabase:newDatabaseName item:nil];
// Update database list
[[self onMainThread] setDatabases:self];
[self endTask];
if (!success) {
SPMainQSync(^{
SPOnewayAlertSheet(

This comment has been minimized.

@dmoagx

dmoagx Jun 19, 2018

Member

SPOnewayAlertSheet doesn't need a thread guard, because it already takes care of that itself.

This comment has been minimized.

@stuconnolly

stuconnolly Jun 20, 2018

Member

Yep, missed that.

NSLocalizedString(@"Unable to copy database", @"unable to copy database message"),
parentWindow,
[NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to copy the database '%@' to '%@'.", @"unable to copy database message informative message"),
[databaseDetails[SPNewDatabaseDetails] databaseName],
newDatabaseName]
);
});
}
}
[dbActionCopy release];
// Update DB list
[self setDatabases:self];
}
#endif
/**
*
* This method *MUST* be called from the UI thread!
*/
- (void)_renameDatabase
@@ -6093,18 +6095,6 @@ - (void)_renameDatabase
}
[dbActionRename release];
#ifdef SP_CODA
if (delegate && [delegate respondsToSelector:@selector(refreshDatabasePopup)]) {
[delegate performSelector:@selector(refreshDatabasePopup) withObject:nil];
}
if (delegate && [delegate respondsToSelector:@selector(selectDatabaseInPopup:)]) {
if ([allDatabases count] > 0 ) {
[delegate performSelector:@selector(selectDatabaseInPopup:) withObject:newDatabaseName];
}
}
#endif
}
/**

0 comments on commit dc2eec2

Please sign in to comment.