aboutsummaryrefslogtreecommitdiffstats
path: root/Source/TableContent.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/TableContent.m')
-rw-r--r--Source/TableContent.m672
1 files changed, 345 insertions, 327 deletions
diff --git a/Source/TableContent.m b/Source/TableContent.m
index 90fbe5d1..fcc6653f 100644
--- a/Source/TableContent.m
+++ b/Source/TableContent.m
@@ -190,7 +190,7 @@
}
// Post a notification that a query will be performed
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
// Set up the table details for the new table, and trigger an interface update
NSDictionary *tableDetails = [NSDictionary dictionaryWithObjectsAndKeys:
@@ -226,7 +226,7 @@
// Init copyTable with necessary information for copying selected rows as SQL INSERT
[tableContentView setTableInstance:self withTableData:tableValues withColumns:dataColumns withTableName:selectedTable withConnection:mySQLConnection];
// Post the notification that the query is finished
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
// Clear any details to restore now that they have been restored
[self clearDetailsToRestore];
@@ -562,7 +562,7 @@
[countText setStringValue:NSLocalizedString(@"Loading table data...", @"Loading table data string")];
// Notify any listeners that a query has started
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
// Start construction of the query string
queryString = [NSMutableString stringWithFormat:@"SELECT %@ FROM %@", [self fieldListForQuery], [selectedTable backtickQuotedString]];
@@ -669,7 +669,7 @@
[self updatePaginationState];
// Notify listenters that the query has finished
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
// Trigger a full reload if required
if (fullTableReloadRequired) [self reloadTable:self];
@@ -1323,7 +1323,7 @@
if ( [tableContentView numberOfSelectedRows] < 1 )
return;
if ( [tableContentView numberOfSelectedRows] > 1 ) {
- SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"You can only copy single rows.", @"message of panel when trying to copy multiple rows"));
+ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"You can only copy single rows.", @"message of panel when trying to copy multiple rows"));
return;
}
@@ -1428,9 +1428,302 @@
[alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the selected %ld rows from this table? This action cannot be undone.", @"delete rows informative message"), (long)[tableContentView numberOfSelectedRows]]];
}
- [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
+ [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeRowSheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
}
+/**
+ * Perform the requested row deletion action.
+ */
+- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+
+ NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet];
+ NSString *wherePart;
+ NSInteger i, errors;
+ BOOL consoleUpdateStatus;
+ BOOL reloadAfterRemovingRow = [prefs boolForKey:SPReloadAfterRemovingRow];
+
+ // Order out current sheet to suppress overlapping of sheets
+ [[alert window] orderOut:nil];
+
+ if ( [contextInfo isEqualToString:@"removeallrows"] ) {
+ if ( returnCode == NSAlertDefaultReturn ) {
+ //check if the user is currently editing a row
+ if (isEditingRow) {
+ //cancel the edit
+ isEditingRow = NO;
+ // in case the delete fails, make sure we at least stay in a somewhat consistent state
+ [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
+ currentlyEditingRow = -1;
+ }
+
+ [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@", [selectedTable backtickQuotedString]]];
+ if ( ![mySQLConnection queryErrored] ) {
+
+ // Reset auto increment if suppression button was ticked
+ if([[alert suppressionButton] state] == NSOnState)
+ [tableSourceInstance setAutoIncrementTo:@"1"];
+
+ [self reloadTable:self];
+
+ } else {
+ [self performSelector:@selector(showErrorSheetWith:)
+ withObject:[NSArray arrayWithObjects:NSLocalizedString(@"Error", @"error"),
+ [NSString stringWithFormat:NSLocalizedString(@"Couldn't delete rows.\n\nMySQL said: %@", @"message when deleteing all rows failed"),
+ [mySQLConnection getLastErrorMessage]],
+ nil]
+ afterDelay:0.3];
+ }
+ }
+ } else if ( [contextInfo isEqualToString:@"removerow"] ) {
+ if ( returnCode == NSAlertDefaultReturn ) {
+ [selectedRows addIndexes:[tableContentView selectedRowIndexes]];
+
+ //check if the user is currently editing a row
+ if (isEditingRow) {
+ //make sure that only one row is selected. This should never happen
+ if ([selectedRows count]!=1) {
+ NSLog(@"Expected only one selected row, but found %d",[selectedRows count]);
+ }
+ // this code is pretty much taken from the escape key handler
+ if ( isEditingNewRow ) {
+ // since the user is currently editing a new row, we don't actually have to delete any rows from the database
+ // we just have to remove the row from the view (and the store)
+ isEditingRow = NO;
+ isEditingNewRow = NO;
+ tableRowsCount--;
+ [tableValues removeRowAtIndex:currentlyEditingRow];
+ currentlyEditingRow = -1;
+ [self updateCountText];
+ [tableContentView reloadData];
+
+ //deselect the row
+ [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
+
+ // we also don't have to reload the table, since no query went to the database
+ return;
+ } else {
+ //cancel the edit
+ isEditingRow = NO;
+ // in case the delete fails, make sure we at least stay in a somewhat consistent state
+ [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
+ currentlyEditingRow = -1;
+ }
+
+ }
+ [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
+
+ errors = 0;
+
+ // Disable updating of the Console Log window for large number of queries
+ // to speed the deletion
+ consoleUpdateStatus = [[SPQueryController sharedQueryController] allowConsoleUpdate];
+ if([selectedRows count] > 10)
+ [[SPQueryController sharedQueryController] setAllowConsoleUpdate:NO];
+
+ NSUInteger index = [selectedRows firstIndex];
+
+ NSArray *primaryKeyFieldNames = [tableDataInstance primaryKeyColumnNames];
+
+ // If no PRIMARY KEY is found and numberOfSelectedRows > 3 then
+ // check for uniqueness of rows via combining all column values;
+ // if unique then use the all columns as 'primary keys'
+ if([selectedRows count] >3 && primaryKeyFieldNames == nil) {
+ primaryKeyFieldNames = [tableDataInstance columnNames];
+
+ NSInteger numberOfRows = 0;
+ // Get the number of rows in the table
+ MCPResult *r;
+ r = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [selectedTable backtickQuotedString]]];
+ if (![mySQLConnection queryErrored]) {
+ NSArray *a = [r fetchRowAsArray];
+ if([a count])
+ numberOfRows = [[a objectAtIndex:0] integerValue];
+ }
+ // Check for uniqueness via LIMIT numberOfRows-1,numberOfRows for speed
+ if(numberOfRows > 0) {
+ [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ GROUP BY %@ LIMIT %ld,%ld", [selectedTable backtickQuotedString], [primaryKeyFieldNames componentsJoinedAndBacktickQuoted], (long)(numberOfRows-1), (long)numberOfRows]];
+ if([mySQLConnection affectedRows] == 0)
+ primaryKeyFieldNames = nil;
+ } else {
+ primaryKeyFieldNames = nil;
+ }
+ }
+
+ if(primaryKeyFieldNames == nil) {
+ // delete row by row
+ while (index != NSNotFound) {
+
+ wherePart = [NSString stringWithString:[self argumentForRow:index]];
+
+ //argumentForRow might return empty query, in which case we shouldn't execute the partial query
+ if([wherePart length]) {
+ [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]];
+
+ // Check for errors
+ if ( ![mySQLConnection affectedRows] || [mySQLConnection queryErrored]) {
+ // If error delete that index from selectedRows for reloading table if
+ // "ReloadAfterRemovingRow" is disbaled
+ if(!reloadAfterRemovingRow)
+ [selectedRows removeIndex:index];
+ errors++;
+ }
+ } else {
+ if(!reloadAfterRemovingRow)
+ [selectedRows removeIndex:index];
+ errors++;
+ }
+ index = [selectedRows indexGreaterThanIndex:index];
+ }
+ } else if ([primaryKeyFieldNames count] == 1) {
+ // if table has only one PRIMARY KEY
+ // delete the fast way by using the PRIMARY KEY in an IN clause
+ NSMutableString *deleteQuery = [NSMutableString string];
+ NSInteger affectedRows = 0;
+
+ [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]];
+
+ while (index != NSNotFound) {
+
+ id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]];
+
+ if([keyValue isKindOfClass:[NSData class]])
+ [deleteQuery appendString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:keyValue]]];
+ else
+ [deleteQuery appendString:[NSString stringWithFormat:@"'%@'", [keyValue description]]];
+
+ // Split deletion query into 256k chunks
+ if([deleteQuery length] > 256000) {
+ [deleteQuery appendString:@")"];
+ [mySQLConnection queryString:deleteQuery];
+ // Remember affected rows for error checking
+ affectedRows += [mySQLConnection affectedRows];
+ // Reinit a new deletion query
+ [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]];
+ } else {
+ [deleteQuery appendString:@","];
+ }
+
+ index = [selectedRows indexGreaterThanIndex:index];
+ }
+
+ // Check if deleteQuery's maximal length was reached for the last index
+ // if yes omit the empty query
+ if(![deleteQuery hasSuffix:@"("]) {
+ // Replace final , by ) and delete the remaining rows
+ [deleteQuery setString:[NSString stringWithFormat:@"%@)", [deleteQuery substringToIndex:([deleteQuery length]-1)]]];
+ [mySQLConnection queryString:deleteQuery];
+ // Remember affected rows for error checking
+ affectedRows += [mySQLConnection affectedRows];
+ }
+
+ errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count];
+ } else {
+ // if table has more than one PRIMARY KEY
+ // delete the row by using all PRIMARY KEYs in an OR clause
+ NSMutableString *deleteQuery = [NSMutableString string];
+ NSInteger affectedRows = 0;
+
+ [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]];
+
+ while (index != NSNotFound) {
+
+ // Build the AND clause of PRIMARY KEYS
+ [deleteQuery appendString:@"("];
+ for(NSString *primaryKeyFieldName in primaryKeyFieldNames) {
+
+ id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]];
+
+ [deleteQuery appendString:[primaryKeyFieldName backtickQuotedString]];
+ if ([keyValue isKindOfClass:[NSData class]]) {
+ [deleteQuery appendString:@"=X'"];
+ [deleteQuery appendString:[mySQLConnection prepareBinaryData:keyValue]];
+ } else {
+ [deleteQuery appendString:@"='"];
+ [deleteQuery appendString:[mySQLConnection prepareString:[keyValue description]]];
+ }
+ [deleteQuery appendString:@"' AND "];
+ }
+
+ // Remove the trailing AND and add the closing bracket
+ [deleteQuery deleteCharactersInRange:NSMakeRange([deleteQuery length]-5, 5)];
+ [deleteQuery appendString:@")"];
+
+ // Split deletion query into 64k chunks
+ if([deleteQuery length] > 64000) {
+ [mySQLConnection queryString:deleteQuery];
+ // Remember affected rows for error checking
+ affectedRows += [mySQLConnection affectedRows];
+ // Reinit a new deletion query
+ [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]];
+ } else {
+ [deleteQuery appendString:@" OR "];
+ }
+
+ index = [selectedRows indexGreaterThanIndex:index];
+ }
+
+ // Check if deleteQuery's maximal length was reached for the last index
+ // if yes omit the empty query
+ if(![deleteQuery hasSuffix:@"WHERE "]) {
+ // Remove final ' OR ' and delete the remaining rows
+ [deleteQuery setString:[deleteQuery substringToIndex:([deleteQuery length]-4)]];
+ [mySQLConnection queryString:deleteQuery];
+ // Remember affected rows for error checking
+ affectedRows += [mySQLConnection affectedRows];
+ }
+
+ errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count];
+ }
+
+ // Restore Console Log window's updating bahaviour
+ [[SPQueryController sharedQueryController] setAllowConsoleUpdate:consoleUpdateStatus];
+
+ if (errors) {
+ NSArray *message;
+ //TODO: The following three messages are NOT localisable!
+ if (errors < 0) {
+ message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
+ [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ more %@ deleted! Please check the Console and inform the Sequel Pro team!", @"message of panel when more rows were deleted"), (long)(errors*-1), ((errors*-1)>1)?@"s":@"", (errors>1)?@"were":@"was"],
+ nil];
+ }
+ else {
+ if (primaryKeyFieldNames == nil)
+ message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
+ [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and use a primary key for your table.", @"message of panel when not all selected fields have been deleted"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s"],
+ nil];
+ else
+ message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
+ [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and check the Console for possible errors inside the primary key%@ for your table.", @"message of panel when not all selected fields have been deleted by using primary keys"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s", (errors>1)?@"s":@""],
+ nil];
+ }
+
+ [self performSelector:@selector(showErrorSheetWith:)
+ withObject:message
+ afterDelay:0.3];
+ }
+
+ // Refresh table content
+ if ( errors || reloadAfterRemovingRow ) {
+ previousTableRowsCount = tableRowsCount;
+ [self loadTableValues];
+ } else {
+ for ( i = tableRowsCount - 1 ; i >= 0 ; i-- ) {
+ if ([selectedRows containsIndex:i]) [tableValues removeRowAtIndex:i];
+ }
+ tableRowsCount = [tableValues count];
+ [tableContentView reloadData];
+ }
+ [tableContentView deselectAll:self];
+ } else {
+ // The user clicked cancel in the "sure you wanna delete" message
+ // restore editing or whatever
+ }
+
+ }
+}
+
+
// Accessors
/**
@@ -1798,13 +2091,13 @@
return YES;
}
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
// If editing, compare the new row to the old row and if they are identical finish editing without saving.
if (!isEditingNewRow && [oldRow isEqualToArray:[tableValues rowContentsAtIndex:currentlyEditingRow]]) {
isEditingRow = NO;
currentlyEditingRow = -1;
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
return YES;
}
@@ -1904,12 +2197,12 @@
}
[fieldValues release];
- [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
+ [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance];
// If no rows have been changed, show error if appropriate.
if ( ![mySQLConnection affectedRows] && ![mySQLConnection queryErrored] ) {
if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) {
- SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil,
+ SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil,
NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db"));
} else {
NSBeep();
@@ -1958,12 +2251,40 @@
// Report errors which have occurred
} else {
- SPBeginAlertSheet(NSLocalizedString(@"Couldn't write row", @"Couldn't write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addrow",
+ SPBeginAlertSheet(NSLocalizedString(@"Couldn't write row", @"Couldn't write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil,
[NSString stringWithFormat:NSLocalizedString(@"MySQL said:\n\n%@", @"message of panel when error while adding row to db"), [mySQLConnection getLastErrorMessage]]);
return NO;
}
}
+/*
+ * Handle the user decision as a result of an addRow error.
+ */
+- (void) addRowErrorSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ // Order out current sheet to suppress overlapping of sheets
+ [[alert window] orderOut:nil];
+
+ // Edit row selected - reselect the row, and start editing.
+ if ( returnCode == NSAlertDefaultReturn ) {
+ [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO];
+ [tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0];
+
+ // Discard changes selected
+ } else {
+ if ( !isEditingNewRow ) {
+ [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
+ isEditingRow = NO;
+ } else {
+ tableRowsCount--;
+ [tableValues removeRowAtIndex:currentlyEditingRow];
+ isEditingRow = NO;
+ isEditingNewRow = NO;
+ }
+ currentlyEditingRow = -1;
+ }
+ [tableContentView reloadData];
+}
/*
* A method to be called whenever the table selection changes; checks whether the current
@@ -2038,7 +2359,7 @@
// When the option to not show blob or text options is set, we have a problem - we don't have
// the right values to use in the WHERE statement. Throw an error if this is the case.
if ( [prefs boolForKey:SPLoadBlobsAsNeeded] && [self tableContainsBlobOrTextColumns] ) {
- SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil,
+ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil,
NSLocalizedString(@"You can't hide blob and text fields when working with tables without index.", @"message of panel when trying to edit tables without index and with hidden blob/text fields"));
[keys removeAllObjects];
[tableContentView deselectAll:self];
@@ -2130,315 +2451,12 @@
}
}
-- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
/*
- if contextInfo == addrow: remain in edit-mode if user hits OK, otherwise cancel editing
- if contextInfo == removerow: removes row if user hits OK
+ * Close an open sheet.
*/
+- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
{
-
- NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet];
- NSString *wherePart;
- NSInteger i, errors;
- BOOL consoleUpdateStatus;
- BOOL reloadAfterRemovingRow = [prefs boolForKey:SPReloadAfterRemovingRow];
-
- if([sheet respondsToSelector:@selector(orderOut:)])
- [sheet orderOut:self];
- else if([sheet window] && [[sheet window] respondsToSelector:@selector(orderOut:)])
- [[sheet window] orderOut:self];
-
- if ( [contextInfo isEqualToString:@"addrow"] ) {
-
- if ( returnCode == NSAlertDefaultReturn ) {
- [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO];
- [tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0];
- } else {
- if ( !isEditingNewRow ) {
- [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
- isEditingRow = NO;
- } else {
- tableRowsCount--;
- [tableValues removeRowAtIndex:currentlyEditingRow];
- isEditingRow = NO;
- isEditingNewRow = NO;
- }
- currentlyEditingRow = -1;
- }
- [tableContentView reloadData];
- } else if ( [contextInfo isEqualToString:@"removeallrows"] ) {
- if ( returnCode == NSAlertDefaultReturn ) {
- //check if the user is currently editing a row
- if (isEditingRow) {
- //cancel the edit
- isEditingRow = NO;
- // in case the delete fails, make sure we at least stay in a somewhat consistent state
- [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
- currentlyEditingRow = -1;
- }
- [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@", [selectedTable backtickQuotedString]]];
- if ( ![mySQLConnection queryErrored] ) {
-
- // Reset auto increment if suppression button was ticked
- if([[sheet suppressionButton] state] == NSOnState)
- [tableSourceInstance setAutoIncrementTo:@"1"];
-
- [self reloadTable:self];
-
- } else {
- [self performSelector:@selector(showErrorSheetWith:)
- withObject:[NSArray arrayWithObjects:NSLocalizedString(@"Error", @"error"),
- [NSString stringWithFormat:NSLocalizedString(@"Couldn't remove rows.\n\nMySQL said: %@", @"message of panel when field cannot be removed"),
- [mySQLConnection getLastErrorMessage]],
- nil]
- afterDelay:0.3];
- }
- }
- } else if ( [contextInfo isEqualToString:@"removerow"] ) {
- if ( returnCode == NSAlertDefaultReturn ) {
- [selectedRows addIndexes:[tableContentView selectedRowIndexes]];
-
- //check if the user is currently editing a row
- if (isEditingRow) {
- //make sure that only one row is selected. This should never happen
- if ([selectedRows count]!=1) {
- NSLog(@"Expected only one selected row, but found %d",[selectedRows count]);
- }
- // this code is pretty much taken from the escape key handler
- if ( isEditingNewRow ) {
- // since the user is currently editing a new row, we don't actually have to delete any rows from the database
- // we just have to remove the row from the view (and the store)
- isEditingRow = NO;
- isEditingNewRow = NO;
- tableRowsCount--;
- [tableValues removeRowAtIndex:currentlyEditingRow];
- currentlyEditingRow = -1;
- [self updateCountText];
- [tableContentView reloadData];
-
- //deselect the row
- [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
-
- // we also don't have to reload the table, since no query went to the database
- return;
- } else {
- //cancel the edit
- isEditingRow = NO;
- // in case the delete fails, make sure we at least stay in a somewhat consistent state
- [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
- currentlyEditingRow = -1;
- }
-
- }
- [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
-
- errors = 0;
-
- // Disable updating of the Console Log window for large number of queries
- // to speed the deletion
- consoleUpdateStatus = [[SPQueryController sharedQueryController] allowConsoleUpdate];
- if([selectedRows count] > 10)
- [[SPQueryController sharedQueryController] setAllowConsoleUpdate:NO];
-
- NSUInteger index = [selectedRows firstIndex];
-
- NSArray *primaryKeyFieldNames = [tableDataInstance primaryKeyColumnNames];
-
- // If no PRIMARY KEY is found and numberOfSelectedRows > 3 then
- // check for uniqueness of rows via combining all column values;
- // if unique then use the all columns as 'primary keys'
- if([selectedRows count] >3 && primaryKeyFieldNames == nil) {
- primaryKeyFieldNames = [tableDataInstance columnNames];
-
- NSInteger numberOfRows = 0;
- // Get the number of rows in the table
- MCPResult *r;
- r = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [selectedTable backtickQuotedString]]];
- if (![mySQLConnection queryErrored]) {
- NSArray *a = [r fetchRowAsArray];
- if([a count])
- numberOfRows = [[a objectAtIndex:0] integerValue];
- }
- // Check for uniqueness via LIMIT numberOfRows-1,numberOfRows for speed
- if(numberOfRows > 0) {
- [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ GROUP BY %@ LIMIT %ld,%ld", [selectedTable backtickQuotedString], [primaryKeyFieldNames componentsJoinedAndBacktickQuoted], (long)(numberOfRows-1), (long)numberOfRows]];
- if([mySQLConnection affectedRows] == 0)
- primaryKeyFieldNames = nil;
- } else {
- primaryKeyFieldNames = nil;
- }
- }
-
- if(primaryKeyFieldNames == nil) {
- // delete row by row
- while (index != NSNotFound) {
-
- wherePart = [NSString stringWithString:[self argumentForRow:index]];
-
- //argumentForRow might return empty query, in which case we shouldn't execute the partial query
- if([wherePart length]) {
- [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]];
-
- // Check for errors
- if ( ![mySQLConnection affectedRows] || [mySQLConnection queryErrored]) {
- // If error delete that index from selectedRows for reloading table if
- // "ReloadAfterRemovingRow" is disbaled
- if(!reloadAfterRemovingRow)
- [selectedRows removeIndex:index];
- errors++;
- }
- } else {
- if(!reloadAfterRemovingRow)
- [selectedRows removeIndex:index];
- errors++;
- }
- index = [selectedRows indexGreaterThanIndex:index];
- }
- } else if ([primaryKeyFieldNames count] == 1) {
- // if table has only one PRIMARY KEY
- // delete the fast way by using the PRIMARY KEY in an IN clause
- NSMutableString *deleteQuery = [NSMutableString string];
- NSInteger affectedRows = 0;
-
- [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]];
-
- while (index != NSNotFound) {
-
- id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]];
-
- if([keyValue isKindOfClass:[NSData class]])
- [deleteQuery appendString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:keyValue]]];
- else
- [deleteQuery appendString:[NSString stringWithFormat:@"'%@'", [keyValue description]]];
-
- // Split deletion query into 256k chunks
- if([deleteQuery length] > 256000) {
- [deleteQuery appendString:@")"];
- [mySQLConnection queryString:deleteQuery];
- // Remember affected rows for error checking
- affectedRows += [mySQLConnection affectedRows];
- // Reinit a new deletion query
- [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]];
- } else {
- [deleteQuery appendString:@","];
- }
-
- index = [selectedRows indexGreaterThanIndex:index];
- }
-
- // Check if deleteQuery's maximal length was reached for the last index
- // if yes omit the empty query
- if(![deleteQuery hasSuffix:@"("]) {
- // Replace final , by ) and delete the remaining rows
- [deleteQuery setString:[NSString stringWithFormat:@"%@)", [deleteQuery substringToIndex:([deleteQuery length]-1)]]];
- [mySQLConnection queryString:deleteQuery];
- // Remember affected rows for error checking
- affectedRows += [mySQLConnection affectedRows];
- }
-
- errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count];
- } else {
- // if table has more than one PRIMARY KEY
- // delete the row by using all PRIMARY KEYs in an OR clause
- NSMutableString *deleteQuery = [NSMutableString string];
- NSInteger affectedRows = 0;
-
- [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]];
-
- while (index != NSNotFound) {
-
- // Build the AND clause of PRIMARY KEYS
- [deleteQuery appendString:@"("];
- for(NSString *primaryKeyFieldName in primaryKeyFieldNames) {
-
- id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]];
-
- [deleteQuery appendString:[primaryKeyFieldName backtickQuotedString]];
- if ([keyValue isKindOfClass:[NSData class]]) {
- [deleteQuery appendString:@"=X'"];
- [deleteQuery appendString:[mySQLConnection prepareBinaryData:keyValue]];
- } else {
- [deleteQuery appendString:@"='"];
- [deleteQuery appendString:[mySQLConnection prepareString:[keyValue description]]];
- }
- [deleteQuery appendString:@"' AND "];
- }
-
- // Remove the trailing AND and add the closing bracket
- [deleteQuery deleteCharactersInRange:NSMakeRange([deleteQuery length]-5, 5)];
- [deleteQuery appendString:@")"];
-
- // Split deletion query into 64k chunks
- if([deleteQuery length] > 64000) {
- [mySQLConnection queryString:deleteQuery];
- // Remember affected rows for error checking
- affectedRows += [mySQLConnection affectedRows];
- // Reinit a new deletion query
- [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]];
- } else {
- [deleteQuery appendString:@" OR "];
- }
-
- index = [selectedRows indexGreaterThanIndex:index];
- }
-
- // Check if deleteQuery's maximal length was reached for the last index
- // if yes omit the empty query
- if(![deleteQuery hasSuffix:@"WHERE "]) {
- // Remove final ' OR ' and delete the remaining rows
- [deleteQuery setString:[deleteQuery substringToIndex:([deleteQuery length]-4)]];
- [mySQLConnection queryString:deleteQuery];
- // Remember affected rows for error checking
- affectedRows += [mySQLConnection affectedRows];
- }
-
- errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count];
- }
-
- // Restore Console Log window's updating bahaviour
- [[SPQueryController sharedQueryController] setAllowConsoleUpdate:consoleUpdateStatus];
-
- if (errors) {
- NSArray *message;
- //TODO: The following three messages are NOT localisable!
- if (errors < 0) {
- message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
- [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ more %@ removed! Please check the Console and inform the Sequel Pro team!", @"message of panel when more rows were deleted"), (long)(errors*-1), ((errors*-1)>1)?@"s":@"", (errors>1)?@"were":@"was"],
- nil];
- }
- else {
- if (primaryKeyFieldNames == nil)
- message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
- [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been removed. Reload the table to be sure that the rows exist and use a primary key for your table.", @"message of panel when not all selected fields have been deleted"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s"],
- nil];
- else
- message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"),
- [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been removed. Reload the table to be sure that the rows exist and check the Console for possible errors inside the primary key%@ for your table.", @"message of panel when not all selected fields have been deleted by using primary keys"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s", (errors>1)?@"s":@""],
- nil];
- }
-
- [self performSelector:@selector(showErrorSheetWith:)
- withObject:message
- afterDelay:0.3];
- }
-
- // Refresh table content
- if ( errors || reloadAfterRemovingRow ) {
- previousTableRowsCount = tableRowsCount;
- [self loadTableValues];
- } else {
- for ( i = tableRowsCount - 1 ; i >= 0 ; i-- ) {
- if ([selectedRows containsIndex:i]) [tableValues removeRowAtIndex:i];
- }
- tableRowsCount = [tableValues count];
- [tableContentView reloadData];
- }
- [tableContentView deselectAll:self];
- } else {
- // The user clicked cancel in the "sure you wanna delete" message
- // restore editing or whatever
- }
- }
+ [sheet orderOut:self];
}
/**
@@ -2449,7 +2467,7 @@
{
// error := first object is the title , second the message, only one button OK
SPBeginAlertSheet([error objectAtIndex:0], NSLocalizedString(@"OK", @"OK button"),
- nil, nil, tableWindow, self, nil, nil, nil,
+ nil, nil, tableWindow, self, nil, nil,
[error objectAtIndex:1]);
}
@@ -2643,8 +2661,8 @@
maxNumRowsIsEstimate = NO;
[tableDataInstance setStatusValue:[NSString stringWithFormat:@"%ld", (long)maxNumRows] forKey:@"Rows"];
[tableDataInstance setStatusValue:@"y" forKey:@"RowsCountAccurate"];
- [tableInfoInstance tableChanged:nil];
- [[tableDocumentInstance valueForKey:@"extendedTableInfoInstance"] performSelectorOnMainThread:@selector(loadTable:) withObject:selectedTable waitUntilDone:YES];
+ [[tableInfoInstance onMainThread] tableChanged:nil];
+ [[[tableDocumentInstance valueForKey:@"extendedTableInfoInstance"] onMainThread] loadTable:selectedTable];
// Otherwise, if the table status value is accurate, use it
} else if ([[tableDataInstance statusValueForKey:@"RowsCountAccurate"] boolValue]) {
@@ -2662,8 +2680,8 @@
maxNumRowsIsEstimate = NO;
[tableDataInstance setStatusValue:[NSString stringWithFormat:@"%ld", (long)maxNumRows] forKey:@"Rows"];
[tableDataInstance setStatusValue:@"y" forKey:@"RowsCountAccurate"];
- [tableInfoInstance tableChanged:nil];
- [[tableDocumentInstance valueForKey:@"extendedTableInfoInstance"] performSelectorOnMainThread:@selector(loadTable:) withObject:selectedTable waitUntilDone:YES];
+ [[tableInfoInstance onMainThread] tableChanged:nil];
+ [[[tableDocumentInstance valueForKey:@"extendedTableInfoInstance"] onMainThread] loadTable:selectedTable];
// Use the estimate count
} else {
@@ -2685,7 +2703,7 @@
maxNumRows = foundMaxRows;
maxNumRowsIsEstimate = NO;
}
- } else if (!isInterruptedLoad && tableRowsCount < [prefs integerForKey:SPLimitResultsValue]) {
+ } else if (!isInterruptedLoad && !isFiltered && tableRowsCount < [prefs integerForKey:SPLimitResultsValue]) {
maxNumRows = foundMaxRows;
maxNumRowsIsEstimate = NO;
}
@@ -2695,7 +2713,7 @@
}
[tableDataInstance setStatusValue:[NSString stringWithFormat:@"%ld", (long)maxNumRows] forKey:@"Rows"];
[tableDataInstance setStatusValue:maxNumRowsIsEstimate?@"n":@"y" forKey:@"RowsCountAccurate"];
- [tableInfoInstance tableChanged:nil];
+ [[tableInfoInstance onMainThread] tableChanged:nil];
}
}
@@ -2926,7 +2944,7 @@
[self loadTableValues];
if ([mySQLConnection queryErrored]) {
- SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil,
+ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil,
[NSString stringWithFormat:NSLocalizedString(@"Couldn't sort table. MySQL said: %@", @"message of panel when sorting of table failed"), [mySQLConnection getLastErrorMessage]]);
[tableDocumentInstance endTask];
[sortPool drain];
@@ -3021,7 +3039,7 @@
MCPResult *tempResult = [mySQLConnection queryString:query];
if (![tempResult numOfRows]) {
- SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil,
+ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil,
NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed"));
return NO;
}