aboutsummaryrefslogtreecommitdiffstats
path: root/Source/CustomQuery.m
diff options
context:
space:
mode:
authorBibiko <bibiko@eva.mpg.de>2009-06-04 14:11:55 +0000
committerBibiko <bibiko@eva.mpg.de>2009-06-04 14:11:55 +0000
commitfa3a82bd89eee142c575e13e47eb6a7bd5e1fc89 (patch)
treeee2536c1bafc17235d0d9d68ed5df3e100236319 /Source/CustomQuery.m
parentf3e421d2e4ba9ec54878968c34044dce89dc6f09 (diff)
downloadsequelpro-fa3a82bd89eee142c575e13e47eb6a7bd5e1fc89.tar.gz
sequelpro-fa3a82bd89eee142c575e13e47eb6a7bd5e1fc89.tar.bz2
sequelpro-fa3a82bd89eee142c575e13e47eb6a7bd5e1fc89.zip
• added support for the “delimiter” command
- added (NSArray *) splitSqlStringByCharacter:(unichar)character; (NSArray *) splitSqlStringIntoRangesByCharacter:(unichar)character; (long) firstOccurrenceInSqlOfCharacter: to the SQLParser which recognize a “delimiter” command • queryAtPosition now works with ranges to speed it up - the current query ranges resp. the just activated query range are cached in order to avoid parsing if the user only navigates through the textView buffer, or if the user calls Run Prev/Current Query only • the "import dump" function makes usage of that new “delimiter” support - i.e. dumps with procs/funcs declaration could be imported IMPORTANT: Please check the new SQLParser exhaustively in order to prove that new approach
Diffstat (limited to 'Source/CustomQuery.m')
-rw-r--r--Source/CustomQuery.m206
1 files changed, 119 insertions, 87 deletions
diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m
index bbcd6f2f..8e7d3325 100644
--- a/Source/CustomQuery.m
+++ b/Source/CustomQuery.m
@@ -59,7 +59,7 @@
// Retrieve the custom query string and split it into separate SQL queries
queryParser = [[SPSQLParser alloc] initWithString:[textView string]];
- queries = [queryParser parseQueries];
+ queries = [queryParser splitSqlStringByCharacter:';'];
[queryParser release];
NSRange curRange = [textView selectedRange];
@@ -97,8 +97,10 @@
// If the current selection is a single caret position, run the current query.
if (selectedRange.length == 0) {
- BOOL doLookBehind = YES;
- query = [self queryAtPosition:selectedRange.location lookBehind:&doLookBehind];
+ // BOOL doLookBehind = YES;
+ // query = [self queryAtPosition:selectedRange.location lookBehind:&doLookBehind];
+ if(currentQueryRange.length)
+ query = [[textView string] substringWithRange:currentQueryRange];
if (!query) {
NSBeep();
return;
@@ -108,7 +110,7 @@
// Otherwise, run the selected text.
} else {
queryParser = [[SPSQLParser alloc] initWithString:[[textView string] substringWithRange:selectedRange]];
- queries = [queryParser parseQueries];
+ queries = [queryParser splitSqlStringByCharacter:';'];
[queryParser release];
}
@@ -388,6 +390,8 @@
float executionTime = 0;
int firstErrorOccuredInQuery = -1;
BOOL suppressErrorSheet = NO;
+ // NSString *delimiterMatch = @"^\\s*delimiter\\s*$|^\\s*delimiter\\s+\\S+\\s*$";
+ NSCharacterSet *whitespaceAndNewlineSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
// Notify listeners that a query has started
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self];
@@ -403,11 +407,13 @@
[customQueryView removeTableColumn:[theColumns objectAtIndex:0]];
}
+ BOOL queriesSeparatedByDelimiter = NO;
+
// Perform the supplied queries in series
for ( i = 0 ; i < [queries count] ; i++ ) {
// Don't run blank queries, or queries which only contain whitespace.
- if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0)
+ if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:whitespaceAndNewlineSet] length] == 0)
continue;
// Run the query, timing execution (note this also includes network and overhead)
@@ -467,7 +473,10 @@
if(usedQuery)
[usedQuery release];
- usedQuery = [[NSString stringWithString:[queries componentsJoinedByString:@";\n"]] retain];
+ if(!queriesSeparatedByDelimiter)
+ usedQuery = [[NSString stringWithString:[queries componentsJoinedByString:@";\n"]] retain];
+ else // TODO how to combine the query array if “delimiter command” was used?
+ usedQuery = @"";
//perform empty query if no query is given
if ( [queries count] == 0 ) {
@@ -489,15 +498,17 @@
}
//add query to history
- [queryHistoryButton insertItemWithTitle:[queries componentsJoinedByString:@"; "] atIndex:1];
- while ( [queryHistoryButton numberOfItems] > [[prefs objectForKey:@"CustomQueryMaxHistoryItems"] intValue] + 1 ) {
- [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1];
- }
- for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ )
- {
- [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]];
+ if(!queriesSeparatedByDelimiter) { // TODO only add to history if no “delimiter” command was used
+ [queryHistoryButton insertItemWithTitle:[queries componentsJoinedByString:@"; "] atIndex:1];
+ while ( [queryHistoryButton numberOfItems] > [[prefs objectForKey:@"CustomQueryMaxHistoryItems"] intValue] + 1 ) {
+ [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1];
+ }
+ for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ )
+ {
+ [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]];
+ }
+ [prefs setObject:menuItems forKey:@"queryHistory"];
}
- [prefs setObject:menuItems forKey:@"queryHistory"];
// Error checking
if ( [errors length] ) {
@@ -745,7 +756,7 @@
return NSMakeRange(NSNotFound, 0);
}
- NSString * theQuery = [queries objectAtIndex:anIndex];
+ NSString *theQuery = [queries objectAtIndex:anIndex];
// Calculate the text length before that query at index anIndex
long prevQueriesLength = 0;
@@ -772,38 +783,44 @@
- (NSString *)queryAtPosition:(long)position lookBehind:(BOOL *)doLookBehind
{
SPSQLParser *customQueryParser;
- NSArray *queries;
- NSString *query = nil;
- NSRange queryRange;
- int i, j, lastQueryStartPosition, queryPosition = 0;
+ NSArray *queries;
+ NSString *query = nil;
+ NSRange queryRange;
+
+ long i, j, lastQueryStartPosition, queryPosition = 0;
+ long queryCount;
+
+ NSCharacterSet *whitespaceAndNewlineSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
// If the supplied position is negative or beyond the end of the string, return nil.
if (position < 0 || position > [[textView string] length])
return nil;
- // Split the current text into queries
- customQueryParser = [[SPSQLParser alloc] initWithString:[textView string]];
- queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringByCharacter:';']];
-
- // ========= test case for SQL splitting into ranges == START
- // NSArray *qs; int qc;
- // qs = [[NSArray alloc] initWithArray:[customQueryParser splitStringIntoRangesOfSQLQueries]];
- // NSLog(@"---START---");
- // for(qc=0;qc<[qs count];qc++) {
- // NSLog(@"%d:", qc);
- // NSLog(@"%@", [[textView string] substringWithRange:NSRangeFromString([qs objectAtIndex:qc])]);
- // }
- // ========= test case for SQL splitting into ranges == END
- [customQueryParser release];
+ // Split the current text into queries and ranges
+ // only if the textView was really changed, otherwise use the cache
+ if([[textView textStorage] editedMask] != 0) {
+ customQueryParser = [[SPSQLParser alloc] initWithString:[textView string]];
+ queries = [[NSArray alloc] initWithArray:[customQueryParser splitSqlStringIntoRangesByCharacter:';']];
+ if(currentQueryRanges)
+ [currentQueryRanges release];
+ currentQueryRanges = [[NSArray arrayWithArray:queries] retain];
+ [customQueryParser release];
+ } else {
+ queries = [[NSArray alloc] initWithArray:currentQueryRanges];
+ }
- NSCharacterSet *newlineSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ queryCount = [queries count];
// Walk along the array of queries to identify the current query - taking into account
// the extra semicolon at the end of each query
- for (i = 0; i < [queries count]; i++ ) {
+ for (i = 0; i < queryCount; i++ ) {
+
lastQueryStartPosition = queryStartPosition;
- queryStartPosition = queryPosition;
- queryPosition += [[queries objectAtIndex:i] length];
+ queryRange = [[queries objectAtIndex:i] rangeValue];
+ queryStartPosition = queryRange.location;
+ queryPosition = NSMaxRange(queryRange);
+
if (queryPosition >= position) {
// If lookbehind is enabled, check whether the current position could be considered to
@@ -815,70 +832,83 @@
// If the caret is at the very start of the string, always associate
if (position == queryStartPosition) positionAssociatedWithPreviousQuery = YES;
-
+
+ // If the caret is in between a user-defined delimiter whose length is >1, always associate
+ if (!positionAssociatedWithPreviousQuery && i && NSMaxRange([[queries objectAtIndex:i-1] rangeValue]) < position && position < queryStartPosition) positionAssociatedWithPreviousQuery = YES;
+
// Otherwise associate if only whitespace since previous, and a newline before next.
if (!positionAssociatedWithPreviousQuery) {
+ @try{
NSString *stringToPrevious = [[textView string] substringWithRange:NSMakeRange(queryStartPosition, position - queryStartPosition)];
NSString *stringToEnd = [[textView string] substringWithRange:NSMakeRange(position, queryPosition - position)];
- NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet];
- if (![[stringToPrevious stringByTrimmingCharactersInSet:newlineSet] length]) {
+ if (![[stringToPrevious stringByTrimmingCharactersInSet:whitespaceAndNewlineSet] length]) {
for (j = 0; j < [stringToEnd length]; j++) {
if ([whitespaceSet characterIsMember:[stringToEnd characterAtIndex:j]]) continue;
- if ([newlineSet characterIsMember:[stringToEnd characterAtIndex:j]]) {
+ if ([whitespaceAndNewlineSet characterIsMember:[stringToEnd characterAtIndex:j]]) {
positionAssociatedWithPreviousQuery = YES;
}
break;
}
}
+ } @catch(id ae) {}
}
// If there is a previous query and the position should be associated with it, do so.
- if (i && positionAssociatedWithPreviousQuery && [[[queries objectAtIndex:i-1] stringByTrimmingCharactersInSet:newlineSet] length]) {
- query = [NSString stringWithString:[queries objectAtIndex:i-1]];
+ if (i && positionAssociatedWithPreviousQuery && [[[[textView string] substringWithRange:[[queries objectAtIndex:i-1] rangeValue]] stringByTrimmingCharactersInSet:whitespaceAndNewlineSet] length]) {
queryStartPosition = lastQueryStartPosition;
- queryRange=NSMakeRange(queryStartPosition, [[queries objectAtIndex:i-1] length]);
+ queryRange = [[queries objectAtIndex:i-1] rangeValue];
break;
}
// Lookbehind failed - set the pointer to NO so the parent knows.
*doLookBehind = NO;
}
-
- query = [NSString stringWithString:[queries objectAtIndex:i]];
- queryRange=NSMakeRange(queryStartPosition, [[queries objectAtIndex:i] length]);
break;
}
- queryPosition++;
}
// For lookbehinds catch position at the very end of a string ending in a semicolon
- if (*doLookBehind && position == [[textView string] length] && !query)
+ if (*doLookBehind && position == [[textView string] length])
{
- query = [queries lastObject];
- queryRange=NSMakeRange(queryStartPosition, [[queries lastObject] length]);
+ queryRange = [[queries lastObject] rangeValue];
}
[queries release];
+
// Remove all background color attributes
- [[textView textStorage] removeAttribute:NSBackgroundColorAttributeName range:NSMakeRange(0,[[textView string] length])];
+ NSRange textRange = NSMakeRange(0,[[textView string] length]);
+ [[textView textStorage] removeAttribute:NSBackgroundColorAttributeName range:textRange];
- // Ensure the string isn't empty.
- // (We could also strip comments for this check, but that prevents use of conditional comments)
- if ([[query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0)
+ queryRange = NSIntersectionRange(queryRange, textRange);
+ if (!queryRange.length) {
+ currentQueryRange = NSMakeRange(0,0);
return nil;
+ }
+
+ query = [[textView string] substringWithRange:queryRange];
// Highlight by setting a background color the current query
- // and delete leading/trailing white spaces
+ // and ignore leading/trailing white spaces
int biasStart = [query rangeOfRegex:@"^\\s*"].length;
- int biasEnd = [query rangeOfRegex:@"\\s*$"].length;
+ int biasEnd = [query rangeOfRegex:@"\\s*$"].length;
queryRange.location += biasStart;
- queryRange.length -= biasEnd+biasStart;
+ queryRange.length -= biasEnd+biasStart;
+
+ // Ensure the string isn't empty.
+ // (We could also strip comments for this check, but that prevents use of conditional comments)
+ if(queryRange.length < 1 || queryRange.length > [query length]) {
+ currentQueryRange = NSMakeRange(0,0);
+ return nil;
+ }
+
[[textView textStorage] addAttribute: NSBackgroundColorAttributeName
value: [NSColor colorWithDeviceRed:0.95 green:0.95 blue:0.95 alpha:1]
range: queryRange ];
- // Return the located string.
+ currentQueryRange = queryRange;
+
+ // Return the located string
return query;
}
@@ -934,6 +964,7 @@
mySQLConnection = theConnection;
prefs = [NSUserDefaults standardUserDefaults];
+ currentQueryRanges = nil;
if ( [prefs objectForKey:@"queryFavorites"] ) {
queryFavorites = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:@"queryFavorites"]];
@@ -1310,41 +1341,42 @@
// whether the caret is inside a valid query.
if ([textView selectedRange].length == 0) {
int selectionPosition = [textView selectedRange].location;
- int movedRangeStart, movedRangeLength;
- BOOL updateQueryButtons = FALSE;
- NSRange oldSelection;
- NSCharacterSet *whitespaceAndNewlineCharset = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ // int movedRangeStart, movedRangeLength;
+ BOOL updateQueryButtons = TRUE;
+ // NSRange oldSelection;
+ // NSCharacterSet *whitespaceAndNewlineCharset = [NSCharacterSet whitespaceAndNewlineCharacterSet];
// Retrieve the old selection position
- [[[aNotification userInfo] objectForKey:@"NSOldSelectedCharacterRange"] getValue:&oldSelection];
+ // [[[aNotification userInfo] objectForKey:@"NSOldSelectedCharacterRange"] getValue:&oldSelection];
// Only process the query text if the selection previously had length, or moved more than 100 characters,
// or the intervening space contained a semicolon, or typing has been performed with no current query.
// This adds more checks to every keypress, but ensures the majority of the actions don't incur a
// parsing overhead - which is cheap on small text strings but heavy of large queries.
- movedRangeStart = (selectionPosition < oldSelection.location)?selectionPosition:oldSelection.location;
- movedRangeLength = abs(selectionPosition - oldSelection.location);
- if (oldSelection.length > 0) updateQueryButtons = TRUE;
- if (!updateQueryButtons && movedRangeLength > 100) updateQueryButtons = TRUE;
- if (!updateQueryButtons && oldSelection.location > [[textView string] length]) updateQueryButtons = TRUE;
- if (!updateQueryButtons && [[textView string] rangeOfString:@";" options:0 range:NSMakeRange(movedRangeStart, movedRangeLength)].location != NSNotFound) updateQueryButtons = TRUE;
- if (!updateQueryButtons && ![runSelectionButton isEnabled] && selectionPosition > oldSelection.location
- && [[[[textView string] substringWithRange:NSMakeRange(movedRangeStart, movedRangeLength)] stringByTrimmingCharactersInSet:whitespaceAndNewlineCharset] length]) updateQueryButtons = TRUE;
- if (!updateQueryButtons && [[runSelectionButton title] isEqualToString:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")]) {
- int charPosition;
- unichar theChar;
- for (charPosition = selectionPosition; charPosition > 0; charPosition--) {
- theChar = [[textView string] characterAtIndex:charPosition-1];
- if (theChar == ';') {
- updateQueryButtons = TRUE;
- break;
- }
- if (![whitespaceAndNewlineCharset characterIsMember:theChar]) break;
- }
- }
- if (!updateQueryButtons && [[runSelectionButton title] isEqualToString:NSLocalizedString(@"Run Previous", @"Title of button to run query just before text caret in custom query view")]) {
- updateQueryButtons = TRUE;
- }
+ // movedRangeStart = (selectionPosition < oldSelection.location)?selectionPosition:oldSelection.location;
+ // movedRangeLength = abs(selectionPosition - oldSelection.location);
+ // updateQueryButtons = TRUE;
+ // if (oldSelection.length > 0) updateQueryButtons = TRUE;
+ // if (!updateQueryButtons && movedRangeLength > 100) updateQueryButtons = TRUE;
+ // if (!updateQueryButtons && oldSelection.location > [[textView string] length]) updateQueryButtons = TRUE;
+ // if (!updateQueryButtons && [[textView string] rangeOfString:@";" options:0 range:NSMakeRange(movedRangeStart, movedRangeLength)].location != NSNotFound) updateQueryButtons = TRUE;
+ // if (!updateQueryButtons && ![runSelectionButton isEnabled] && selectionPosition > oldSelection.location
+ // && [[[[textView string] substringWithRange:NSMakeRange(movedRangeStart, movedRangeLength)] stringByTrimmingCharactersInSet:whitespaceAndNewlineCharset] length]) updateQueryButtons = TRUE;
+ // if (!updateQueryButtons && [[runSelectionButton title] isEqualToString:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")]) {
+ // int charPosition;
+ // unichar theChar;
+ // for (charPosition = selectionPosition; charPosition > 0; charPosition--) {
+ // theChar = [[textView string] characterAtIndex:charPosition-1];
+ // if (theChar == ';') {
+ // updateQueryButtons = TRUE;
+ // break;
+ // }
+ // if (![whitespaceAndNewlineCharset characterIsMember:theChar]) break;
+ // }
+ // }
+ // if (!updateQueryButtons && [[runSelectionButton title] isEqualToString:NSLocalizedString(@"Run Previous", @"Title of button to run query just before text caret in custom query view")]) {
+ // updateQueryButtons = TRUE;
+ // }
if (updateQueryButtons) {
[runSelectionButton setTitle:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")];