From 3d8accc866c35c41717188f933d77f48c708a69f Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 12 Jun 2015 00:01:01 +0200 Subject: Fix a popular exception when a user tried to edit a field with a lost connection and then chose "disconnect" when Sequel Pro asks to reconnect (attempt at #2137) --- Source/SPCopyTable.m | 5 +-- Source/SPTableContent.m | 73 ++++++++++++++++++++++++++--------------- Source/SPTableContentDelegate.m | 2 +- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index 2b5554b5..439ff84d 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -495,8 +495,9 @@ static const NSInteger kBlobAsImageFile = 4; // If the data is not loaded, attempt to fetch the value if ([cellData isSPNotLoaded] && [[self delegate] isKindOfClass:spTableContentClass]) { + NSString *whereArgument = [tableInstance argumentForRow:rowIndex]; // Abort if no table name given, not table content, or if there are no indices on this table - if (!selectedTable || ![[self delegate] isKindOfClass:spTableContentClass] || ![(NSString*)[tableInstance argumentForRow:rowIndex] length]) { + if (!selectedTable || ![[self delegate] isKindOfClass:spTableContentClass] || ![whereArgument length]) { NSBeep(); free(columnMappings); free(columnTypes); @@ -509,7 +510,7 @@ static const NSInteger kBlobAsImageFile = 4; [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [NSArrayObjectAtIndex(tbHeader, columnMappings[c]) backtickQuotedString], [selectedTable backtickQuotedString], - [tableInstance argumentForRow:rowIndex]]]; + whereArgument]]; } // Check for NULL value diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 6988b0f3..4cd21bb1 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -1884,12 +1884,13 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper if ([prefs boolForKey:SPLoadBlobsAsNeeded]) { // Abort if there are no indices on this table - argumentForRow will display an error. - if (![[self argumentForRow:[tableContentView selectedRow]] length]) { + NSString *whereArgument = [self argumentForRow:[tableContentView selectedRow]]; + if (![whereArgument length]) { return; } // If we have indexes, use argumentForRow - queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@", [selectedTable backtickQuotedString], [self argumentForRow:[tableContentView selectedRow]]]]; + queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@", [selectedTable backtickQuotedString], whereArgument]]; dbDataRow = [queryResult getRowAsArray]; } #endif @@ -2203,9 +2204,14 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper while (anIndex != NSNotFound) { // Build the AND clause of PRIMARY KEYS - [deleteQuery appendString:@"("]; - [deleteQuery appendString:[self argumentForRow:anIndex excludingLimits:YES]]; - [deleteQuery appendString:@")"]; + NSString *whereArg = [self argumentForRow:anIndex excludingLimits:YES]; + if(![whereArg length]) { + SPLog(@"empty WHERE clause not acceptable for DELETE! Abort."); + NSBeep(); + return; + } + + [deleteQuery appendFormat:@"(%@)",whereArg]; // Split deletion query into 64k chunks if([deleteQuery length] > 64000) { @@ -2825,7 +2831,15 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [queryString appendFormat:@"%@ = %@", [NSArrayObjectAtIndex(rowFieldsToSave, i) backtickQuotedString], NSArrayObjectAtIndex(rowValuesToSave, i)]; } - [queryString appendFormat:@" WHERE %@", [self argumentForRow:-2]]; + NSString *whereArg = [self argumentForRow:-2]; + if(![whereArg length]) { + SPLog(@"Did not find plausible WHERE condition for UPDATE."); + NSBeep(); + [rowFieldsToSave release]; + [rowValuesToSave release]; + return NO; + } + [queryString appendFormat:@" WHERE %@", whereArg]; } [rowFieldsToSave release]; @@ -3029,25 +3043,22 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper */ - (NSString *)argumentForRow:(NSInteger)row excludingLimits:(BOOL)excludeLimits { - SPMySQLResult *theResult; - id tempValue; - NSMutableString *value = [NSMutableString string]; - NSMutableString *argument = [NSMutableString string]; - NSArray *columnNames; - NSUInteger i; - if ( row == -1 ) return @""; // Retrieve the field names for this table from the data cache. This is used when requesting all data as part // of the fieldListForQuery method, and also to decide whether or not to preserve the current filter/sort settings. - columnNames = [tableDataInstance columnNames]; + NSArray *columnNames = [tableDataInstance columnNames]; // Get the primary key if there is one if ( !keys ) { setLimit = NO; keys = [[NSMutableArray alloc] init]; - theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; + SPMySQLResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; + if(!theResult) { + SPLog(@"no result from SHOW COLUMNS mysql query! Abort."); + return @""; + } [theResult setReturnDataAsStrings:YES]; for (NSDictionary *eachRow in theResult) { if ( [[eachRow objectForKey:@"Key"] isEqualToString:@"PRI"] ) { @@ -3074,17 +3085,18 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #endif } + NSMutableString *argument = [NSMutableString string]; // Walk through the keys list constructing the argument list - for ( i = 0 ; i < [keys count] ; i++ ) { + for (NSUInteger i = 0 ; i < [keys count] ; i++ ) { if ( i ) [argument appendString:@" AND "]; + id tempValue; // Use the selected row if appropriate if ( row >= 0 ) { tempValue = [tableValues cellDataAtRow:row column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; - - // Otherwise use the oldRow } + // Otherwise use the oldRow else { tempValue = [oldRow objectAtIndex:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; } @@ -3093,25 +3105,34 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [argument appendFormat:@"%@ IS NULL", [NSArrayObjectAtIndex(keys, i) backtickQuotedString]]; } else if ([tempValue isSPNotLoaded]) { - NSLog(@"Exceptional case: SPNotLoaded object found for method “argumentForRow:”!"); + SPLog(@"Exceptional case: SPNotLoaded object found! Abort."); return @""; } else { + NSString *escVal; + NSString *fmt = @"%@"; // If the field is of type BIT then it needs a binary prefix if ([[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"type"] isEqualToString:@"BIT"]) { - [value setString:[NSString stringWithFormat:@"b'%@'", [mySQLConnection escapeString:tempValue includingQuotes:NO]]]; + escVal = [mySQLConnection escapeString:tempValue includingQuotes:NO]; + fmt = @"b'%@'"; } else if ([tempValue isKindOfClass:[SPMySQLGeometryData class]]) { - [value setString:[mySQLConnection escapeAndQuoteData:[tempValue data]]]; + escVal = [mySQLConnection escapeAndQuoteData:[tempValue data]]; } // BLOB/TEXT data else if ([tempValue isKindOfClass:[NSData class]]) { - [value setString:[mySQLConnection escapeAndQuoteData:tempValue]]; + escVal = [mySQLConnection escapeAndQuoteData:tempValue]; } - else - [value setString:[mySQLConnection escapeAndQuoteString:tempValue]]; - - [argument appendFormat:@"%@ = %@", [NSArrayObjectAtIndex(keys, i) backtickQuotedString], value]; + else { + escVal = [mySQLConnection escapeAndQuoteString:tempValue]; + } + + if(!escVal) { + SPLog(@"(row=%ld) nil value for key <%@> is invalid! Abort.",row,NSArrayObjectAtIndex(keys, i)); + return @""; + } + + [argument appendFormat:@"%@ = %@", [NSArrayObjectAtIndex(keys, i) backtickQuotedString], [NSString stringWithFormat:fmt,escVal]]; } } diff --git a/Source/SPTableContentDelegate.m b/Source/SPTableContentDelegate.m index 4d7bbb04..07c355b9 100644 --- a/Source/SPTableContentDelegate.m +++ b/Source/SPTableContentDelegate.m @@ -239,7 +239,7 @@ // issue that the table has no primary key NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; - if ([wherePart length] == 0) return NO; + if (![wherePart length]) return NO; // If the selected cell hasn't been loaded, load it. if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) { -- cgit v1.2.3