diff options
Diffstat (limited to 'Source/SPCustomQuery.m')
-rw-r--r-- | Source/SPCustomQuery.m | 155 |
1 files changed, 79 insertions, 76 deletions
diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 307e5c9a..f69db3b7 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -25,6 +25,7 @@ #import "SPCustomQuery.h" #import "SPSQLParser.h" +#import "SPMySQL.h" #ifndef SP_REFACTOR /* headers */ #import "SPGrowlController.h" #endif @@ -45,6 +46,7 @@ #import "SPGeometryDataView.h" #import "SPAppController.h" #import "SPBundleHTMLOutputController.h" +#include <pthread.h> #ifndef SP_REFACTOR /* headers */ #import <BWToolkitFramework/BWToolkitFramework.h> @@ -537,7 +539,7 @@ /** * Toggle whether the query info pane is visible. */ -- (IBAction)toggleQueryInfoPaneCollapse:(id)sender +- (IBAction)toggleQueryInfoPaneCollapse:(NSButton *)sender { [queryInfoPaneSplitView toggleCollapse:sender]; @@ -582,12 +584,12 @@ - (void)performQueriesTask:(NSDictionary *)taskArguments { - NSAutoreleasePool *queryRunningPool = [[NSAutoreleasePool alloc] init]; - NSArray *queries = [taskArguments objectForKey:@"queries"]; - MCPStreamingResult *streamingResult = nil; - NSMutableString *errors = [NSMutableString string]; - SEL callbackMethod = NULL; - NSString *taskButtonString; + NSAutoreleasePool *queryRunningPool = [[NSAutoreleasePool alloc] init]; + NSArray *queries = [taskArguments objectForKey:@"queries"]; + SPMySQLFastStreamingResult *streamingResult = nil; + NSMutableString *errors = [NSMutableString string]; + SEL callbackMethod = NULL; + NSString *taskButtonString; NSUInteger i, totalQueriesRun = 0, totalAffectedRows = 0; double executionTime = 0; @@ -624,7 +626,7 @@ } // Disable automatic query retries on failure for the custom queries - [mySQLConnection setAllowQueryRetries:NO]; + [mySQLConnection setRetryQueriesOnConnectionFailure:NO]; NSUInteger queryCount = [queries count]; NSMutableArray *tempQueries = [NSMutableArray arrayWithCapacity:queryCount]; @@ -656,16 +658,16 @@ // Run the query, timing execution (note this also includes network and overhead) streamingResult = [[mySQLConnection streamingQueryString:query] retain]; - executionTime += [mySQLConnection lastQueryExecutionTime]; + executionTime += [streamingResult queryExecutionTime]; totalQueriesRun++; // If this is the last query, retrieve and store the result; otherwise, // discard the result without fully loading. - if (totalQueriesRun == queryCount || [mySQLConnection queryCancelled]) { + if (totalQueriesRun == queryCount || [mySQLConnection lastQueryWasCancelled]) { // Retrieve and cache the column definitions for the result array if (cqColumnDefinition) [cqColumnDefinition release]; - cqColumnDefinition = [[streamingResult fetchResultFieldsStructure] retain]; + cqColumnDefinition = [[streamingResult fieldDefinitions] retain]; if(!reloadingExistingResult) { [[self onMainThread] updateTableView]; @@ -692,24 +694,24 @@ } // Record any affected rows - if ( [mySQLConnection affectedRows] != (my_ulonglong)~0 ) - totalAffectedRows += (NSUInteger)[mySQLConnection affectedRows]; - else if ( [streamingResult numOfRows] ) - totalAffectedRows += (NSUInteger)[streamingResult numOfRows]; + if ( [mySQLConnection rowsAffectedByLastQuery] != (unsigned long long)~0 ) + totalAffectedRows += (NSUInteger)[mySQLConnection rowsAffectedByLastQuery]; + else if ( [streamingResult numberOfRows] ) + totalAffectedRows += (NSUInteger)[streamingResult numberOfRows]; [streamingResult release]; // Store any error messages - if ([mySQLConnection queryErrored] || [mySQLConnection queryCancelled]) { + if ([mySQLConnection queryErrored] || [mySQLConnection lastQueryWasCancelled]) { NSString *errorString; - if ([mySQLConnection queryCancelled]) { - if ([mySQLConnection queryCancellationUsedReconnect]) + if ([mySQLConnection lastQueryWasCancelled]) { + if ([mySQLConnection lastQueryWasCancelledUsingReconnect]) errorString = NSLocalizedString(@"Query cancelled. Please note that to cancel the query the connection had to be reset; transactions and connection variables were reset.", @"Query cancel by resetting connection error"); else errorString = NSLocalizedString(@"Query cancelled.", @"Query cancelled error"); } else { - errorString = [mySQLConnection getLastErrorMessage]; + errorString = [mySQLConnection lastErrorMessage]; } // If the query errored, append error to the error log for display at the end @@ -726,7 +728,7 @@ [[errorText onMainThread] setString:errors]; // ask the user to continue after detecting an error - if (![mySQLConnection queryCancelled]) { + if (![mySQLConnection lastQueryWasCancelled]) { [tableDocumentInstance setTaskIndicatorShouldAnimate:NO]; SPBeginWaitingAlertSheet(@"title", @@ -735,7 +737,7 @@ @selector(sheetDidEnd:returnCode:contextInfo:), @"runAllContinueStopSheet", NSLocalizedString(@"MySQL Error", @"mysql error message"), - [mySQLConnection getLastErrorMessage], + [mySQLConnection lastErrorMessage], &runAllContinueStopSheetReturnCode ); [tableDocumentInstance setTaskIndicatorShouldAnimate:YES]; @@ -768,7 +770,7 @@ databaseWasChanged = YES; } // If the query was cancelled, end all queries. - if ([mySQLConnection queryCancelled]) break; + if ([mySQLConnection lastQueryWasCancelled]) break; } // Reload table list if at least one query began with drop, alter, rename, or create @@ -797,7 +799,7 @@ if ( !queryCount ) { streamingResult = [mySQLConnection streamingQueryString:@""]; [streamingResult cancelResultLoad]; - [errors setString:[mySQLConnection getLastErrorMessage]]; + [errors setString:[mySQLConnection lastErrorMessage]]; } // add query to history @@ -814,7 +816,7 @@ // Set up the status string NSString *statusString = nil; NSString *statusErrorString = [errors length]?NSLocalizedString(@"Errors", @"Errors title"):NSLocalizedString(@"No errors", @"No errors title"); - if ( [mySQLConnection queryCancelled] ) { + if ( [mySQLConnection lastQueryWasCancelled] ) { if (totalQueriesRun > 1) { statusString = [NSString stringWithFormat:NSLocalizedString(@"%@; Cancelled in query %ld, after %@", @"text showing multiple queries were cancelled"), statusErrorString, @@ -859,7 +861,7 @@ [[affectedRowsText onMainThread] setStringValue:statusString]; // Restore automatic query retries - [mySQLConnection setAllowQueryRetries:YES]; + [mySQLConnection setRetryQueriesOnConnectionFailure:YES]; #ifndef SP_REFACTOR /* [tableDocumentInstance setQueryMode:] */ [tableDocumentInstance setQueryMode:SPInterfaceQueryMode]; @@ -934,9 +936,8 @@ /** * Processes a supplied streaming result set, loading it into the data array. */ -- (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult +- (void)processResultIntoDataStorage:(SPMySQLFastStreamingResult *)theResult { - NSArray *tempRow; NSAutoreleasePool *dataLoadingPool; // Remove all items from the table @@ -948,7 +949,7 @@ // Set the column count on the data store before setting up anything else - // ensures that SPDataStorage is set up for timer-driven data loads - [resultData setColumnCount:[theResult numOfFields]]; + [resultData setColumnCount:[theResult numberOfFields]]; // Set up the table updates timer [[self onMainThread] initQueryLoadTimer]; @@ -957,10 +958,10 @@ dataLoadingPool = [[NSAutoreleasePool alloc] init]; // Loop through the result rows as they become available - while ((tempRow = [theResult fetchNextRowAsArray])) { + for (NSArray *eachRow in theResult) { pthread_mutex_lock(&resultDataLock); - SPDataStorageAddRow(resultData, tempRow); + SPDataStorageAddRow(resultData, eachRow); resultDataCount++; pthread_mutex_unlock(&resultDataLock); @@ -1286,7 +1287,7 @@ NSInteger firstErrorOccuredInQuery = [[errorDetails objectForKey:@"firstErrorQueryNumber"] integerValue]; // If errors occur, display them - if ( [mySQLConnection queryCancelled] || ([errorsString length] && !queryIsTableSorter)) { + if ( [mySQLConnection lastQueryWasCancelled] || ([errorsString length] && !queryIsTableSorter)) { // set the error text [errorText setString:[errorsString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]; @@ -1297,7 +1298,7 @@ NSRange errorLineNumberRange = [errorsString rangeOfRegex:@"([0-9]+)[^0-9]*$" options:RKLNoOptions inRange:NSMakeRange(0, [errorsString length]) capture:1L error:nil]; // if error ID 1064 and a line number was found - if([mySQLConnection getLastErrorID] == 1064 && errorLineNumberRange.length) + if([mySQLConnection lastErrorID] == 1064 && errorLineNumberRange.length) { // Get the line number NSUInteger errorAtLine = [[errorsString substringWithRange:errorLineNumberRange] integerValue]; @@ -1499,7 +1500,7 @@ /** * Sets the connection (received from SPDatabaseDocument) and makes things that have to be done only once */ -- (void)setConnection:(MCPConnection *)theConnection +- (void)setConnection:(SPMySQLConnection *)theConnection { mySQLConnection = theConnection; currentQueryRanges = nil; @@ -1755,7 +1756,7 @@ [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Checking field data for editing...", @"checking field data for editing task description")]; // Actual check whether field can be identified bijectively - MCPResult *tempResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@.%@ %@", + SPMySQLResult *tempResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@.%@ %@", [[columnDefinition objectForKey:@"db"] backtickQuotedString], [tableForColumn backtickQuotedString], fieldIDQueryStr]]; @@ -1765,7 +1766,7 @@ return [NSArray arrayWithObjects:[NSNumber numberWithInteger:-1], @"", nil]; } - NSArray *tempRow = [tempResult fetchRowAsArray]; + NSArray *tempRow = [tempResult getRowAsArray]; if([tempRow count] && [[tempRow objectAtIndex:0] integerValue] > 1) { // try to identify the cell by using blob data @@ -1785,7 +1786,7 @@ return [NSArray arrayWithObjects:[NSNumber numberWithInteger:-1], @"", nil]; } - tempRow = [tempResult fetchRowAsArray]; + tempRow = [tempResult getRowAsArray]; if([tempRow count] && [[tempRow objectAtIndex:0] integerValue] < 1) { [tableDocumentInstance endTask]; @@ -1808,7 +1809,6 @@ - (NSString *)argumentForRow:(NSUInteger)rowIndex ofTable:(NSString *)tableForColumn andDatabase:(NSString *)database includeBlobs:(BOOL)includeBlobs { NSArray *dataRow; - NSDictionary *theRow; id field; NSMutableArray *argumentParts = [NSMutableArray array]; @@ -1827,17 +1827,14 @@ dataRow = [resultData rowContentsAtIndex:rowIndex]; // Get the primary key if there is one, using any columns present within it - MCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@.%@", + SPMySQLResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@.%@", [database backtickQuotedString], [tableForColumn backtickQuotedString]]]; [theResult setReturnDataAsStrings:YES]; - if ([theResult numOfRows]) [theResult dataSeek:0]; NSMutableArray *primaryColumnsInSpecifiedTable = [NSMutableArray array]; - NSUInteger i; - for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { - theRow = [theResult fetchRowAsDictionary]; - if ( [[theRow objectForKey:@"Key"] isEqualToString:@"PRI"] ) { + for (NSDictionary *eachRow in theResult) { + if ( [[eachRow objectForKey:@"Key"] isEqualToString:@"PRI"] ) { for (field in columnsInSpecifiedTable) { - if([[field objectForKey:@"org_name"] isEqualToString:[theRow objectForKey:@"Field"]]) { + if([[field objectForKey:@"org_name"] isEqualToString:[eachRow objectForKey:@"Field"]]) { [primaryColumnsInSpecifiedTable addObject:field]; } } @@ -1850,34 +1847,38 @@ // Build up the argument for (field in columnsToQuery) { id aValue = [dataRow objectAtIndex:[[field objectForKey:@"datacolumnindex"] integerValue]]; - if ([aValue isKindOfClass:[NSNull class]] || [aValue isNSNull]) { + + if ([aValue isNSNull]) { [argumentParts addObject:[NSString stringWithFormat:@"%@ IS NULL", [[field objectForKey:@"org_name"] backtickQuotedString]]]; - } else { - if ([[field objectForKey:@"typegrouping"] isEqualToString:@"textdata"]) { - if(includeBlobs) { - [fieldIDQueryStr appendFormat:@"%@='%@' AND ", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection prepareString:aValue]]; - } - } - else if ([[field objectForKey:@"typegrouping"] isEqualToString:@"blobdata"] || [[field objectForKey:@"type"] isEqualToString:@"BINARY"] || [[field objectForKey:@"type"] isEqualToString:@"VARBINARY"]) { - if(includeBlobs) { - [fieldIDQueryStr appendFormat:@"%@=X'%@' AND ", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection prepareBinaryData:aValue]]; - } + } + else { + NSString *fieldTypeGrouping = [field objectForKey:@"typegrouping"]; + + // Skip blob-type fields if requested + if (!includeBlobs + && ([fieldTypeGrouping isEqualToString:@"textdata"] || + [fieldTypeGrouping isEqualToString:@"blobdata"] || + [[field objectForKey:@"type"] isEqualToString:@"BINARY"] || + [[field objectForKey:@"type"] isEqualToString:@"VARBINARY"])) + { + continue; } - else if ([[field objectForKey:@"typegrouping"] isEqualToString:@"bit"]) { + + if ([[field objectForKey:@"typegrouping"] isEqualToString:@"bit"]) { [argumentParts addObject:[NSString stringWithFormat:@"%@=b'%@'", [[field objectForKey:@"org_name"] backtickQuotedString], [aValue description]]]; } else if ([[field objectForKey:@"typegrouping"] isEqualToString:@"integer"]) { [fieldIDQueryStr appendFormat:@"%@=%@ AND ", [[field objectForKey:@"org_name"] backtickQuotedString], [aValue description]]; } else if ([[field objectForKey:@"typegrouping"] isEqualToString:@"geometry"]) { - [argumentParts addObject:[NSString stringWithFormat:@"%@=X'%@'", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection prepareBinaryData:[aValue data]]]]; + [argumentParts addObject:[NSString stringWithFormat:@"%@=%@", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection escapeAndQuoteData:[aValue data]]]]; } // BLOB/TEXT data else if ([aValue isKindOfClass:[NSData class]]) { - [argumentParts addObject:[NSString stringWithFormat:@"%@=X'%@'", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection prepareBinaryData:aValue]]]; + [argumentParts addObject:[NSString stringWithFormat:@"%@=%@", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection escapeAndQuoteData:aValue]]]; } else { - [argumentParts addObject:[NSString stringWithFormat:@"%@='%@'", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection prepareString:aValue]]]; + [argumentParts addObject:[NSString stringWithFormat:@"%@=%@", [[field objectForKey:@"org_name"] backtickQuotedString], [mySQLConnection escapeAndQuoteString:aValue]]]; } } } @@ -1914,11 +1915,11 @@ NSString *newObject = nil; if ( [anObject isKindOfClass:[NSCalendarDate class]] ) { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + newObject = [mySQLConnection escapeAndQuoteString:[anObject description]]; } else if ( [anObject isKindOfClass:[NSNumber class]] ) { newObject = [anObject stringValue]; } else if ( [anObject isKindOfClass:[NSData class]] ) { - newObject = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:anObject]]; + newObject = [mySQLConnection escapeAndQuoteData:anObject]; } else { if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) { newObject = @"CURRENT_TIMESTAMP"; @@ -1935,7 +1936,7 @@ && [[anObject description] isEqualToString:@"NOW()"]) { newObject = @"NOW()"; } else { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + newObject = [mySQLConnection escapeAndQuoteString:[anObject description]]; } } @@ -1947,13 +1948,13 @@ // Check for errors while UPDATE if ([mySQLConnection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); + [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection lastErrorMessage]]); return; } // This shouldn't happen – for safety reasons - if ( ![mySQLConnection affectedRows] ) { + if ( ![mySQLConnection rowsAffectedByLastQuery] ) { #ifndef SP_REFACTOR if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, @@ -2330,7 +2331,7 @@ return nil; } } - else if ([theValue isKindOfClass:[MCPGeometryData class]]) { + else if ([theValue isKindOfClass:[SPMySQLGeometryData class]]) { SPGeometryDataView *v = [[SPGeometryDataView alloc] initWithCoordinates:[theValue coordinates]]; image = [v thumbnailImage]; if(image) { @@ -3064,7 +3065,7 @@ if(![searchString length]) return @""; NSRange aRange; - MCPResult *theResult = nil; + SPMySQLResult *theResult = nil; NSDictionary *tableDetails; NSMutableString *theHelp = [NSMutableString string]; @@ -3083,14 +3084,16 @@ return SP_HELP_NOT_AVAILABLE; } // nothing found? - if(![theResult numOfRows]) { + if(![theResult numberOfRows]) { + // try to search via: HELP 'searchString%' theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"HELP '%@%%'", [searchString stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]]]; + // really nothing found? - if(![theResult numOfRows]) + if(![theResult numberOfRows]) return @""; } - tableDetails = [[NSDictionary alloc] initWithDictionary:[theResult fetchRowAsDictionary]]; + tableDetails = [[NSDictionary alloc] initWithDictionary:[theResult getRowAsDictionary]]; if ([tableDetails objectForKey:@"description"]) { // one single help topic found if ([tableDetails objectForKey:@"name"]) { @@ -3165,9 +3168,9 @@ } } else { // list all found topics - NSUInteger i; - NSUInteger r = (NSUInteger)[theResult numOfRows]; - if (r) [theResult dataSeek:0]; + NSUInteger r = (NSUInteger)[theResult numberOfRows]; + if (r) [theResult seekToRow:0]; + // check if HELP 'contents' is called if(![searchString isEqualToString:SP_HELP_TOC_SEARCH_STRING]) [theHelp appendFormat:@"<br><i>%@ “%@”</i><br>", NSLocalizedString(@"Help topics for", @"help topics for"), searchString]; @@ -3176,9 +3179,9 @@ // iterate through all found rows and print them as HTML ul/li list [theHelp appendString:@"<ul>"]; - for ( i = 0 ; i < r ; i++ ) { - NSArray *anArray = [theResult fetchRowAsArray]; - NSString *topic = [anArray objectAtIndex:[anArray count]-2]; + [theResult setDefaultRowReturnType:SPMySQLResultRowAsArray]; + for (NSArray *eachRow in theResult) { + NSString *topic = [eachRow objectAtIndex:[eachRow count]-2]; [theHelp appendFormat:@"<li><a title='%@ “%@”' href='%@' class='internallink'>%@</a></li>", NSLocalizedString(@"Show MySQL help for", @"show mysql help for"), topic, topic, topic]; } @@ -3998,7 +4001,7 @@ value = [prefs objectForKey:SPNullValue]; } - if ([value isKindOfClass:[MCPGeometryData class]]) { + if ([value isKindOfClass:[SPMySQLGeometryData class]]) { value = [value wktString]; } |