aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2010-02-05 01:51:17 +0000
committerrowanbeentje <rowan@beent.je>2010-02-05 01:51:17 +0000
commitf880dea7369cb6ce57c552cd359f871bf7e2e414 (patch)
treee7ae9cecd00dd89dc8a3e66f0ce362fa2c023d6d /Source
parent557d012ed48835423c36bcd0c10754ea3f7e2d3b (diff)
downloadsequelpro-f880dea7369cb6ce57c552cd359f871bf7e2e414.tar.gz
sequelpro-f880dea7369cb6ce57c552cd359f871bf7e2e414.tar.bz2
sequelpro-f880dea7369cb6ce57c552cd359f871bf7e2e414.zip
- Rework SPSQLParser, extending DELIMITER support into all the original functions (off by default). Remove the forked "...Sql..." functions, as they're now duplicates, and switch CustomQuery to using the original methods.
- TableDump imports can now process DELIMITERs correctly as a result. - Alter the TableDump display of tables etc to use TablesList as the source of information, and used cached lists where appropriate for a small speedup. Also means we gain consistent sorting. - Display procedures and functions in the toggleable list when exporting as SQL - Tweak the procedure and function export to only export selected items, and also to respect the "export drop syntax" and "export create syntax" checkboxes - Fix a crash when removing items from the TablesList resulted in an errorneous selection by deselecting all rows before deleting (and preemptively applying the same fix to TableContent)
Diffstat (limited to 'Source')
-rw-r--r--Source/CustomQuery.m12
-rw-r--r--Source/SPSQLParser.h38
-rw-r--r--Source/SPSQLParser.m484
-rw-r--r--Source/SPTableData.m4
-rw-r--r--Source/TableContent.m1
-rw-r--r--Source/TableDump.h8
-rw-r--r--Source/TableDump.m134
-rw-r--r--Source/TablesList.m1
8 files changed, 313 insertions, 369 deletions
diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m
index a891f35e..2d1dae4d 100644
--- a/Source/CustomQuery.m
+++ b/Source/CustomQuery.m
@@ -67,7 +67,8 @@
// Retrieve the custom query string and split it into separate SQL queries
queryParser = [[SPSQLParser alloc] initWithString:[textView string]];
- queries = [queryParser splitSqlStringByCharacter:';'];
+ [queryParser setDelimiterSupport:YES];
+ queries = [queryParser splitStringByCharacter:';'];
[queryParser release];
oldThreadedQueryRange = [textView selectedRange];
@@ -135,7 +136,8 @@
// Otherwise, run the selected text.
} else {
queryParser = [[SPSQLParser alloc] initWithString:[[textView string] substringWithRange:selectedRange]];
- queries = [queryParser splitSqlStringByCharacter:';'];
+ [queryParser setDelimiterSupport:YES];
+ queries = [queryParser splitStringByCharacter:';'];
[queryParser release];
// Remember query start position for error highlighting
@@ -965,7 +967,8 @@
// 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:';']];
+ [customQueryParser setDelimiterSupport:YES];
+ queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringIntoRangesByCharacter:';']];
numberOfQueries = [queries count];
if(currentQueryRanges)
[currentQueryRanges release];
@@ -1078,7 +1081,8 @@
// Split the current text into ranges of queries
customQueryParser = [[SPSQLParser alloc] initWithString:[[textView string] substringWithRange:NSMakeRange(position, [[textView string] length]-position)]];
- queries = [[NSArray alloc] initWithArray:[customQueryParser splitSqlStringIntoRangesByCharacter:';']];
+ [customQueryParser setDelimiterSupport:YES];
+ queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringIntoRangesByCharacter:';']];
[customQueryParser release];
// Check for a valid index
diff --git a/Source/SPSQLParser.h b/Source/SPSQLParser.h
index a3ae4224..1331d60f 100644
--- a/Source/SPSQLParser.h
+++ b/Source/SPSQLParser.h
@@ -64,11 +64,12 @@
NSInteger parsedToPosition;
NSInteger charCacheStart;
NSInteger charCacheEnd;
- NSString *delimiter;
- NSInteger delimiterLength;
- BOOL charIsDelimiter;
- BOOL isDelimiterCommand;
BOOL ignoreCommentStrings;
+
+ BOOL supportDelimiters;
+ NSString *delimiter;
+ NSUInteger delimiterLengthMinusOne;
+ BOOL lastMatchIsDelimiter;
}
typedef enum _SPCommentTypes {
@@ -77,7 +78,23 @@ typedef enum _SPCommentTypes {
SPCStyleComment = 2
} SPCommentType;
-- (void) setIgnoringCommentStrings:(BOOL)ignoringCommentStrings;
+/*
+ * Set whether comment strings should be ignored during parsing.
+ * Normally, comment strings are treated as dead space and ignored;
+ * for certain parsing operations, characters within comments need
+ * to be inspected, and setIgnoreCommentStrings can be set to YES to
+ * achieve this.
+ */
+- (void) setIgnoreCommentStrings:(BOOL)ignoringCommentStrings;
+
+/*
+ * Set whether DELIMITER support should be enabled while parsing.
+ * This is off by default; when switched on, delimiters commands will
+ * be parsed out and not returned to the calling class, and any active
+ * delimiter statements will be used to override the supplied character
+ * for many commands.
+ */
+- (void) setDelimiterSupport:(BOOL)shouldSupportDelimiters;
/*
* Removes comments within the current string, trimming "#", "--[/s]", and "⁄* *⁄" style strings.
@@ -217,8 +234,13 @@ typedef enum _SPCommentTypes {
*/
- (NSArray *) splitStringByCharacter:(unichar)character skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings;
-- (NSArray *) splitSqlStringByCharacter:(unichar)character;
-- (NSArray *) splitSqlStringIntoRangesByCharacter:(unichar)character;
+/*
+ * As splitStringByCharacter:, but returning only the ranges of queries, stored as NSValues.
+ * Quoted strings are automatically ignored when looking for the characters.
+ * SQL comments are automatically ignored when looking for the characters.
+ * Returns an array with one range covering the entire string if the supplied character is not found.
+ */
+- (NSArray *) splitStringIntoRangesByCharacter:(unichar)character;
/*
* Methods used internally by this class to power the methods above:
@@ -226,7 +248,6 @@ typedef enum _SPCommentTypes {
- (NSUInteger) firstOccurrenceOfCharacter:(unichar)character ignoringQuotedStrings:(BOOL)ignoreQuotedStrings;
- (NSUInteger) firstOccurrenceOfCharacter:(unichar)character afterIndex:(NSInteger)startIndex ignoringQuotedStrings:(BOOL)ignoreQuotedStrings;
- (NSUInteger) firstOccurrenceOfCharacter:(unichar)character afterIndex:(NSInteger)startIndex skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings;
-- (NSUInteger) firstOccurrenceInSqlOfCharacter:(unichar)character afterIndex:(NSInteger)startIndex skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings;
- (NSUInteger) endIndexOfStringQuotedByCharacter:(unichar)quoteCharacter startingAtIndex:(NSInteger)index;
- (NSUInteger) endIndexOfCommentOfType:(SPCommentType)commentType startingAtIndex:(NSInteger)index;
@@ -250,6 +271,7 @@ typedef enum _SPCommentTypes {
- (id) initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding;
- (id) initWithFormat:(NSString *)format, ...;
- (id) initWithFormat:(NSString *)format arguments:(va_list)argList;
+- (void) initSQLExtensions;
- (NSUInteger) length;
- (unichar) characterAtIndex:(NSUInteger)index;
- (id) description;
diff --git a/Source/SPSQLParser.m b/Source/SPSQLParser.m
index c93f3198..fab0ab7f 100644
--- a/Source/SPSQLParser.m
+++ b/Source/SPSQLParser.m
@@ -42,12 +42,25 @@ TO_BUFFER_STATE to_scan_string (const char *);
*/
@implementation SPSQLParser : NSMutableString
-- (void)setIgnoringCommentStrings:(BOOL)ignoringCommentStrings
+/*
+ * Control whether comment strings should be skipped during parsing.
+ */
+- (void)setIgnoreCommentStrings:(BOOL)ignoringCommentStrings
{
ignoreCommentStrings = ignoringCommentStrings;
}
/*
+ * Control whether DELIMITER commands are recognised and used to override
+ * supported characters.
+ */
+- (void) setDelimiterSupport:(BOOL)shouldSupportDelimiters
+{
+ supportDelimiters = shouldSupportDelimiters;
+}
+
+
+/*
* Removes comments within the current string, trimming "#", "--[/s]", and "⁄* *⁄" style strings.
*/
- (void) deleteComments
@@ -167,10 +180,12 @@ TO_BUFFER_STATE to_scan_string (const char *);
*/
- (BOOL) trimToCharacter:(unichar)character inclusively:(BOOL)inclusive ignoringQuotedStrings:(BOOL)ignoreQuotedStrings
{
- NSUInteger stringIndex;
+ NSUInteger stringIndex = -1;
// Get the first occurrence of the specified character, returning NO if not found
- stringIndex = [self firstOccurrenceOfCharacter:character ignoringQuotedStrings:ignoreQuotedStrings];
+ do {
+ stringIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && stringIndex != NSNotFound);
if (stringIndex == NSNotFound) return NO;
// If it has been found, trim the string appropriately and return YES
@@ -193,14 +208,18 @@ TO_BUFFER_STATE to_scan_string (const char *);
* are ignored.
*/
- (NSString *) stringToCharacter:(unichar)character inclusively:(BOOL)inclusive ignoringQuotedStrings:(BOOL)ignoreQuotedStrings {
- NSUInteger stringIndex;
+ NSUInteger stringIndex = -1;
+ NSUInteger returnFromPosition;
// Get the first occurrence of the specified character, returning nil if not found
- stringIndex = [self firstOccurrenceOfCharacter:character ignoringQuotedStrings:ignoreQuotedStrings];
+ do {
+ returnFromPosition = stringIndex + 1;
+ stringIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && stringIndex != NSNotFound);
if (stringIndex == NSNotFound) return nil;
// If it has been found, return the appropriate string range
- return [string substringWithRange:NSMakeRange(0, stringIndex + (inclusive?1:0))];
+ return [string substringWithRange:NSMakeRange(returnFromPosition, stringIndex + (inclusive?1:0) - returnFromPosition)];
}
@@ -220,7 +239,8 @@ TO_BUFFER_STATE to_scan_string (const char *);
*/
- (NSString *) trimAndReturnStringToCharacter:(unichar)character trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn ignoringQuotedStrings:(BOOL)ignoreQuotedStrings
{
- NSUInteger stringIndex;
+ NSUInteger returnFromPosition;
+ NSUInteger stringIndex = 0;
NSString *resultString;
if (character != parsedToChar) {
@@ -229,11 +249,14 @@ TO_BUFFER_STATE to_scan_string (const char *);
}
// Get the first occurrence of the specified character, returning nil if it could not be found
- stringIndex = [self firstOccurrenceOfCharacter:character afterIndex:parsedToPosition ignoringQuotedStrings:ignoreQuotedStrings];
+ do {
+ returnFromPosition = stringIndex;
+ stringIndex = [self firstOccurrenceOfCharacter:character afterIndex:parsedToPosition ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && stringIndex != NSNotFound);
if (stringIndex == NSNotFound) return nil;
// Select the appropriate string range, truncate the current string, and return the selected string
- resultString = [NSString stringWithString:[string substringWithRange:NSMakeRange(0, stringIndex + (inclusiveReturn?1:0))]];
+ resultString = [NSString stringWithString:[string substringWithRange:NSMakeRange(returnFromPosition, stringIndex + (inclusiveReturn?1:0) - returnFromPosition)]];
[self deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))];
return resultString;
}
@@ -275,14 +298,19 @@ TO_BUFFER_STATE to_scan_string (const char *);
*/
- (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings
{
- NSUInteger fromCharacterIndex, toCharacterIndex;
+ NSUInteger toCharacterIndex, fromCharacterIndex = -1;
// Look for the first occurrence of the from: character
- fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ do {
+ fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && fromCharacterIndex != NSNotFound);
if (fromCharacterIndex == NSNotFound) return nil;
// Look for the first/balancing occurrence of the to: character
- toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ toCharacterIndex = fromCharacterIndex;
+ do {
+ toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:toCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && toCharacterIndex != NSNotFound);
if (toCharacterIndex == NSNotFound) return nil;
// Return the correct part of the string.
@@ -326,15 +354,20 @@ TO_BUFFER_STATE to_scan_string (const char *);
*/
- (NSString *) trimAndReturnStringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings
{
- NSUInteger fromCharacterIndex, toCharacterIndex;
+ NSUInteger fromCharacterIndex = -1, toCharacterIndex;
NSString *resultString;
// Look for the first occurrence of the from: character
- fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ do {
+ fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && fromCharacterIndex != NSNotFound);
if (fromCharacterIndex == NSNotFound) return nil;
// Look for the first/balancing occurrence of the to: character
- toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ toCharacterIndex = fromCharacterIndex;
+ do {
+ toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:toCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
+ } while (lastMatchIsDelimiter && toCharacterIndex != NSNotFound);
if (toCharacterIndex == NSNotFound) return nil;
// Select the correct part of the string, truncate the current string, and return the selected string.
@@ -378,116 +411,88 @@ TO_BUFFER_STATE to_scan_string (const char *);
NSMutableArray *resultsArray = [NSMutableArray array];
NSInteger stringIndex = -1;
NSUInteger nextIndex = 0;
-
- // Walk through the string finding the character to split by, and add all strings to the array.
- while (1) {
- nextIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings];
- if (nextIndex == NSNotFound) {
- break;
- }
-
- [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]];
- stringIndex = nextIndex;
- }
-
- // Add the end of the string after the previously matched character where appropriate.
- if (stringIndex + 1 < [string length]) {
- [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, [string length] - stringIndex - 1)]];
- }
-
- return resultsArray;
-}
-
-/*
- * As splitStringByCharacter: ..., but allows control over both bracketing and quoting.
- */
-- (NSArray *) splitSqlStringByCharacter:(unichar)character
-{
- NSMutableArray *resultsArray = [NSMutableArray arrayWithCapacity:2000];
-
- NSInteger stringIndex = -1;
- NSUInteger nextIndex = 0;
NSInteger queryLength;
- // these delimiter variables will be set in firstOccurrenceOfCharacter:
- delimiter = nil;
- delimiterLength = 0; // is delimiter length minus 1
- charIsDelimiter = YES; // flag if passed character is the current delimiter
- isDelimiterCommand = NO;
-
- IMP firstOccOfChar = [self methodForSelector:@selector(firstOccurrenceInSqlOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:)];
+ IMP firstOccOfChar = [self methodForSelector:@selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:)];
IMP subString = [string methodForSelector:@selector(substringWithRange:)];
-
+
// Walk through the string finding the character to split by, and add all strings to the array.
while (1) {
-
- nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceInSqlOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, NO, YES);
+ nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, skipBrackets, ignoreQuotedStrings);
+ while (lastMatchIsDelimiter && nextIndex != NSNotFound) {
+ stringIndex = nextIndex;
+ nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, skipBrackets, ignoreQuotedStrings);
+ }
if (nextIndex == NSNotFound)
break;
- stringIndex += 1;
- // Ignore a delimiter command and check range length
- queryLength = nextIndex - stringIndex - delimiterLength;
- if(!isDelimiterCommand && queryLength > 0)
- [resultsArray addObject:(NSString *)(*subString)(string, @selector(substringWithRange:), NSMakeRange(stringIndex, queryLength))];
- if(isDelimiterCommand) isDelimiterCommand = NO;
+ // Add queries to the result array if they have a length
+ stringIndex++;
+ queryLength = nextIndex - stringIndex - delimiterLengthMinusOne;
+ if (queryLength > 0)
+ CFArrayAppendValue((CFMutableArrayRef)resultsArray, (NSString *)(*subString)(string, @selector(substringWithRange:), NSMakeRange(stringIndex, queryLength)));
+
stringIndex = nextIndex;
}
- // Add the end of the string after the previously matched character where appropriate
- // if it does not contain only white space characters and if the last query is not a
- // "delimiter" statement to avoid unnecessary error messages.
+ // Add the end of the string after the previously matched character where appropriate.
if (stringIndex + 1 < [string length]) {
- NSString *lastQuery = [[string substringFromIndex:stringIndex + 1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if([lastQuery length] && ![lastQuery isMatchedByRegex:@"(?i)^\\s*delimiter\\s+\\S+"])
- [resultsArray addObject:lastQuery];
+ NSString *finalQuery = [[string substringFromIndex:stringIndex + 1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if (supportDelimiters && [finalQuery isMatchedByRegex:@"(?i)^\\s*delimiter\\s+\\S+"])
+ finalQuery = nil;
+ if ([finalQuery length])
+ [resultsArray addObject:finalQuery];
}
-
+
return resultsArray;
}
+
/*
- * As splitStringByCharacter: but it returns only the ranges of queries as NSValues
+ * As splitStringByCharacter:, but returning only the ranges of queries, stored as NSValues.
*/
-- (NSArray *) splitSqlStringIntoRangesByCharacter:(unichar)character
+- (NSArray *) splitStringIntoRangesByCharacter:(unichar)character
{
- NSMutableArray *resultsArray = [NSMutableArray arrayWithCapacity:2000];
-
+ NSMutableArray *resultsArray = [NSMutableArray array];
NSInteger stringIndex = -1;
NSUInteger nextIndex = 0;
NSInteger queryLength;
- // these delimiter variables will be set in firstOccurrenceOfCharacter:
- delimiter = nil;
- delimiterLength = 0; // is delimiter length minus 1
- charIsDelimiter = YES; // flag if passed character is the current delimiter
- isDelimiterCommand = NO;
+ IMP firstOccOfChar = [self methodForSelector:@selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:)];
- IMP firstOccOfChar = [self methodForSelector:@selector(firstOccurrenceInSqlOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:)];
-
- // Walk through the string finding the character to split by, and add all strings to the array.
+ // Walk through the string finding the character to split by, and add all ranges to the array.
while (1) {
-
- nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceInSqlOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, NO, YES);
+ nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, NO, YES);
+ while (lastMatchIsDelimiter && nextIndex != NSNotFound) {
+ stringIndex = nextIndex;
+ nextIndex = (NSUInteger)(*firstOccOfChar)(self, @selector(firstOccurrenceOfCharacter:afterIndex:skippingBrackets:ignoringQuotedStrings:), character, stringIndex, NO, YES);
+ }
if (nextIndex == NSNotFound)
break;
- stringIndex += 1;
- // Ignore a delimiter command and check range length
- queryLength = nextIndex - stringIndex - delimiterLength;
- if(!isDelimiterCommand && queryLength > 0)
- [resultsArray addObject:[NSValue valueWithRange:NSMakeRange(stringIndex, queryLength)]];
- if(isDelimiterCommand) isDelimiterCommand = NO;
+ // Add ranges to the result array if they have a length
+ stringIndex++;
+ queryLength = nextIndex - stringIndex - delimiterLengthMinusOne;
+ if (queryLength > 0)
+ CFArrayAppendValue((CFMutableArrayRef)resultsArray, [NSValue valueWithRange:NSMakeRange(stringIndex, queryLength)]);
+
stringIndex = nextIndex;
}
// Add the end of the string after the previously matched character where appropriate.
- if (stringIndex + 1 < [string length])
- [resultsArray addObject:[NSValue valueWithRange:NSMakeRange(stringIndex + 1, [string length] - stringIndex - 1)]];
+ stringIndex++;
+ if (stringIndex < [string length]) {
+ NSString *finalQuery = [[string substringFromIndex:stringIndex] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if (supportDelimiters && [finalQuery isMatchedByRegex:@"(?i)^\\s*delimiter\\s+\\S+"])
+ finalQuery = nil;
+ if ([finalQuery length])
+ [resultsArray addObject:[NSValue valueWithRange:NSMakeRange(stringIndex, [string length] - stringIndex - delimiterLengthMinusOne)]];
+ }
return resultsArray;
}
+
/*
* A method intended for use by the functions above.
*/
@@ -512,10 +517,12 @@ TO_BUFFER_STATE to_scan_string (const char *);
unichar currentCharacter;
NSUInteger stringLength = [string length];
NSInteger bracketingLevel = 0;
+ lastMatchIsDelimiter = NO;
// Cache frequently used selectors, avoiding dynamic binding overhead
IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)];
IMP endIndex = [self methodForSelector:@selector(endIndexOfStringQuotedByCharacter:startingAtIndex:)];
+ IMP substringWithRange = [self methodForSelector:@selector(substringWithRange:)];
// Sanity check inputs
if (startIndex < -1) startIndex = -1;
@@ -525,7 +532,15 @@ TO_BUFFER_STATE to_scan_string (const char *);
currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex);
// Check for the ending character, and if it has been found and quoting/brackets is valid, return.
- if (currentCharacter == character) {
+ // If delimiter support is active and a delimiter is set, check for the delimiter
+ if (supportDelimiters && delimiter) {
+ if (currentStringIndex >= delimiterLengthMinusOne && [delimiter isEqualToString:(NSString *)(*substringWithRange)(self, @selector(substringWithRange:), NSMakeRange(currentStringIndex - delimiterLengthMinusOne, delimiterLengthMinusOne + 1))]) {
+ if (!skipBrackets || bracketingLevel <= 0) {
+ parsedToPosition = currentStringIndex;
+ return currentStringIndex;
+ }
+ }
+ } else if (currentCharacter == character) {
if (!skipBrackets || bracketingLevel <= 0) {
parsedToPosition = currentStringIndex;
return currentStringIndex;
@@ -577,140 +592,66 @@ TO_BUFFER_STATE to_scan_string (const char *);
if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '*') break;
currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex];
break;
- }
- }
-
- // If no matches have been made in this string, return NSNotFound.
- parsedToPosition = stringLength - 1;
- return NSNotFound;
-}
-
-/*
- * Look for the first occurence of a char and reset the split char on runtime
- * via “delimiter” command for splitSqlStringIntoRangesByCharacter: and splitSqlStringByCharacter.
- */
-- (NSUInteger) firstOccurrenceInSqlOfCharacter:(unichar)character afterIndex:(NSInteger)startIndex skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings
-{
- NSUInteger currentStringIndex, quotedStringEndIndex;
- unichar currentCharacter;
- NSUInteger stringLength = [string length];
- NSInteger bracketingLevel = 0;
-
- // Cache frequently used selectors, avoiding dynamic binding overhead
- IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)];
- IMP endIndex = [self methodForSelector:@selector(endIndexOfStringQuotedByCharacter:startingAtIndex:)];
-
- // Sanity check inputs
- if (startIndex < -1) startIndex = -1;
-
- // Walk along the string, processing characters
- for (currentStringIndex = startIndex + 1; currentStringIndex < stringLength; currentStringIndex++) {
- currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex);
-
- // Check for the ending character, and if it has been found and quoting/brackets is valid, return.
- // no “delimiter” is set by the user
- if (charIsDelimiter)
- {
- if(currentCharacter == character)
- if (!skipBrackets || bracketingLevel <= 0)
- return currentStringIndex;
- }
- // a “delimiter” other than 'character' is set by the user
- else
- {
- if([[self substringWithRange:NSMakeRange(currentStringIndex - delimiterLength, delimiterLength + 1)] isEqualToString:delimiter])
- if (!skipBrackets || bracketingLevel <= 0)
- return currentStringIndex;
- }
-
- // Process strings and comments as appropriate
- switch (currentCharacter) {
-
- // When quote characters are encountered and strings are not being ignored, walk to the end of the quoted string.
- case '\'':
- case '"':
- case '`':
- if (!ignoreQuotedStrings) break;
- quotedStringEndIndex = (NSUInteger)(*endIndex)(self, @selector(endIndexOfStringQuotedByCharacter:startingAtIndex:), currentCharacter, currentStringIndex+1);
- if (quotedStringEndIndex == NSNotFound) {
- return NSNotFound;
- }
- currentStringIndex = quotedStringEndIndex;
- break;
-
- // For opening brackets increment the bracket count
- case '(':
- bracketingLevel++;
- break;
-
- // For closing brackets decrement the bracket count
- case ')':
- bracketingLevel--;
-
- // For comments starting "--[\s]", ensure the start syntax is valid before proceeding.
- case '-':
- if (stringLength < currentStringIndex + 2) break;
- if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '-') break;
- if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+2)]) break;
- currentStringIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex];
- break;
-
- case '#':
- if(ignoreCommentStrings) break;
- currentStringIndex = [self endIndexOfCommentOfType:SPHashComment startingAtIndex:currentStringIndex];
- break;
- // For comments starting "/*", ensure the start syntax is valid before proceeding.
- case '/':
- if(ignoreCommentStrings) break;
- if (stringLength < currentStringIndex + 1) break;
- if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '*') break;
- currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex];
- break;
+ // Check for delimiter strings, by first checking letter-by-letter to "deli" for speed (as there's no default
+ // commands which start with it), and then switching to regex for simplicty.
case 'd':
- case 'D': // only parse to “deli” because there's no default command which begins with it; then check via regex
- // Check for length of “elimiter x\s”
- if (stringLength >= currentStringIndex + 11) {
- // Check for “(^|\s)delimiter”
- if(currentStringIndex == 0
- || (currentStringIndex && [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex-1)])) {
- NSArray *delimiterString;
- switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1)) {
- case 'e':
- case 'E':
- switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+2)) {
- case 'l':
- case 'L':
- switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+3)) {
- case 'i':
- case 'I':
- if([self isMatchedByRegex:@"^(delimiter[ \\t]+(\\S+))(?=\\s)"
- options:RKLCaseless
- inRange:NSMakeRange(currentStringIndex, stringLength - currentStringIndex)
- error:nil]) {
- isDelimiterCommand = YES;
- delimiterString = [[self arrayOfCaptureComponentsMatchedByRegex:@"(?i)^(delimiter[ \\t]+(\\S+))(?=\\s)"
- range:NSMakeRange(currentStringIndex, stringLength - currentStringIndex)] objectAtIndex:0];
- delimiter = [delimiterString objectAtIndex:2];
- delimiterLength = [delimiter length] - 1;
- charIsDelimiter = ([delimiter isEqualToString:[NSString stringWithFormat:@"%C", character]]);
- return currentStringIndex + [[delimiterString objectAtIndex:1] length] - delimiterLength;
+ case 'D':
+
+ // Only proceed if delimiter support is enabled and the remaining string is long enough,
+ // and that the "d" is the start of a word
+ if (supportDelimiters && stringLength >= currentStringIndex + 11
+ && (currentStringIndex == 0
+ || [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex-1)]))
+ {
+ switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1)) {
+ case 'e':
+ case 'E':
+ switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+2)) {
+ case 'l':
+ case 'L':
+ switch((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+3)) {
+ case 'i':
+ case 'I':
+ if([self isMatchedByRegex:@"^(delimiter[ \\t]+(\\S+))(?=\\s)"
+ options:RKLCaseless
+ inRange:NSMakeRange(currentStringIndex, stringLength - currentStringIndex)
+ error:nil])
+ {
+
+ // Delimiter command found. Extract the delimiter string itself
+ NSArray *delimiterCommandParts = [[self arrayOfCaptureComponentsMatchedByRegex:@"(?i)^(delimiter[ \\t]+(\\S+))(?=\\s)"
+ range:NSMakeRange(currentStringIndex, stringLength - currentStringIndex)] objectAtIndex:0];
+ delimiter = [delimiterCommandParts objectAtIndex:2];
+ delimiterLengthMinusOne = [delimiter length] - 1;
+ parsedToPosition = currentStringIndex + [[delimiterCommandParts objectAtIndex:1] length];
+
+ // Drop back to standard non-delimiter mode if the delimiter has ended
+ if ([delimiter isEqualToString:[NSString stringWithFormat:@"%C", character]]) {
+ delimiter = nil;
+ delimiterLengthMinusOne = 0;
}
- default: break;
- }
+
+ // With the internal state updated, return the match, clearly marked as a delimiter
+ lastMatchIsDelimiter = YES;
+ return parsedToPosition;
+ }
default: break;
}
default: break;
}
- }
+ default: break;
}
+ }
}
}
// If no matches have been made in this string, return NSNotFound.
+ parsedToPosition = stringLength - 1;
return NSNotFound;
}
+
/*
* A method intended for use by the functions above.
*/
@@ -870,71 +811,52 @@ TO_BUFFER_STATE to_scan_string (const char *);
if (self = [super init]) {
string = [[NSMutableString string] retain];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
- ignoreCommentStrings = NO;
-
+ [self initSQLExtensions];
return self;
}
- (id) initWithBytes:(const void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithBytes:bytes length:length encoding:encoding];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)flag {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithBytesNoCopy:bytes length:length encoding:encoding freeWhenDone:flag];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithCapacity:(NSUInteger)capacity {
if (self = [super init]) {
string = [[NSMutableString stringWithCapacity:capacity] retain];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)flag {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:flag];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithContentsOfFile:(id)path {
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
return [self initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
}
- (id) initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)encoding error:(NSError **)error {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithContentsOfFile:path encoding:encoding error:error];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithCString:nullTerminatedCString encoding:encoding];
}
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return self;
}
- (id) initWithFormat:(NSString *)format, ... {
@@ -942,19 +864,26 @@ TO_BUFFER_STATE to_scan_string (const char *);
va_start(argList, format);
id str = [self initWithFormat:format arguments:argList];
va_end(argList);
- parsedToChar = '\0';
- parsedToPosition = -1;
- charCacheEnd = -1;
+ [self initSQLExtensions];
return str;
}
- (id) initWithFormat:(NSString *)format arguments:(va_list)argList {
if (self = [super init]) {
string = [[NSMutableString alloc] initWithFormat:format arguments:argList];
}
+ [self initSQLExtensions];
+ return self;
+}
+- (void) initSQLExtensions {
parsedToChar = '\0';
parsedToPosition = -1;
charCacheEnd = -1;
- return self;
+ ignoreCommentStrings = NO;
+ supportDelimiters = NO;
+ delimiter = nil;
+ delimiterLengthMinusOne = 0;
+ lastMatchIsDelimiter = NO;
+
}
- (NSUInteger) length {
return [string length];
@@ -971,6 +900,9 @@ TO_BUFFER_STATE to_scan_string (const char *);
}
- (void) setString:(NSString *)aString {
[string setString:aString];
+ delimiter = nil;
+ delimiterLengthMinusOne = 0;
+ lastMatchIsDelimiter = NO;
[self clearCharCache];
}
- (void) replaceCharactersInRange:(NSRange)range withString:(NSString *)aString {
@@ -983,76 +915,4 @@ TO_BUFFER_STATE to_scan_string (const char *);
[super dealloc];
}
-@end
-
-/*
- * As splitStringByCharacter: ..., but allows control over quoting
- * - it recognises CREATE ... BEGIN ... END statements
- * - it can detect a SINGLE SQL statement in between
- * delimiter foo ... foo delimiter ;
- * ['delimiter ;' MUST be given!]
- * - it returns an array of ranges (as NSString "{loc, length}").
- * FromPosition: is needed if a subrange is passed to sync the ranges
- * according to the CQ textView ones.
- */
-// - (NSArray *) splitStringIntoRangesOfSQLQueries
-// {
-// return [self splitStringIntoRangesOfSQLQueriesFromPosition:0];
-// }
-// - (NSArray *) splitStringIntoRangesOfSQLQueriesFromPosition:(long)position
-// {
-// NSMutableArray *resultsArray = [NSMutableArray array];
-//
-// //initialise flex
-// yyuoffset = 0; yyuleng = 0;
-// to_switch_to_buffer(to_scan_string([string UTF8String]));
-//
-// unsigned long token;
-// unsigned long lastFoundToken = 0;
-// unsigned long delimLength = 0;
-// unsigned long commentStart = 0;
-// unsigned long commentLength = 0;
-//
-// NSString *delimString;
-//
-// //now loop through all queries
-// while (token=tolex()){
-// switch (token) {
-// case SP_SQL_TOKEN_SEMICOLON:
-// [resultsArray addObject:[NSString stringWithFormat:@"{%d,%d}", lastFoundToken+position, yyuoffset-lastFoundToken]];
-// break;
-// case SP_SQL_TOKEN_SINGLE_LINE_COMMENT:
-// commentStart = yyuoffset+position;
-// commentLength = yyuleng;
-// break;
-// case SP_SQL_TOKEN_DELIM_END:
-// [resultsArray addObject:[NSString stringWithFormat:@"{%d,%d}", lastFoundToken+position, yyuoffset-lastFoundToken-delimLength]];
-// delimLength = 0;
-// delimString = nil;
-// break;
-// case SP_SQL_TOKEN_DELIM_VALUE:
-// delimString = [string substringWithRange:NSMakeRange(yyuoffset,yyuleng)];
-// // NSLog(@"del: %@", delimString);
-// delimLength = yyuleng;
-// break;
-// case SP_SQL_TOKEN_COMPOUND_END:
-// [resultsArray addObject:[NSString stringWithFormat:@"{%d,%d}", lastFoundToken+position, yyuoffset+yyuleng-lastFoundToken]];
-// break;
-// default:
-// continue;
-// }
-// if(token<SP_SQL_TOKEN_IGNORE)
-// {
-// lastFoundToken = yyuoffset+yyuleng;
-// // ignore sinlge comment lines at the very beginning of a query
-// if(commentStart == lastFoundToken)
-// lastFoundToken += commentLength;
-// }
-// }
-//
-// // add the last text chunk as query
-// if(lastFoundToken+1<[self length])
-// [resultsArray addObject:[NSString stringWithFormat:@"{%d,%d}", lastFoundToken+position, [self length]-lastFoundToken-delimLength]];
-//
-// return resultsArray;
-// }
+@end \ No newline at end of file
diff --git a/Source/SPTableData.m b/Source/SPTableData.m
index 2c6136a3..965ec042 100644
--- a/Source/SPTableData.m
+++ b/Source/SPTableData.m
@@ -388,7 +388,7 @@
// Capture the area between the two backticks as the name
// Set the parser to ignoreCommentStrings since a field name can contain # or /*
- [fieldsParser setIgnoringCommentStrings:YES];
+ [fieldsParser setIgnoreCommentStrings:YES];
NSString *fieldName = [fieldsParser trimAndReturnStringFromCharacter: quoteCharacter
toCharacter: quoteCharacter
trimmingInclusively: YES
@@ -415,7 +415,7 @@
ignoringQuotedStrings: NO]
];
}
- [fieldsParser setIgnoringCommentStrings:NO];
+ [fieldsParser setIgnoreCommentStrings:NO];
[tableColumn setObject:[NSNumber numberWithInteger:[tableColumns count]] forKey:@"datacolumnindex"];
[tableColumn setObject:fieldName forKey:@"name"];
diff --git a/Source/TableContent.m b/Source/TableContent.m
index 4cc573c4..50bcfd9f 100644
--- a/Source/TableContent.m
+++ b/Source/TableContent.m
@@ -2111,6 +2111,7 @@
errors = 0;
[selectedRows addIndexes:[tableContentView selectedRowIndexes]];
+ [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
// Disable updating of the Console Log window for large number of queries
// to speed the deletion
diff --git a/Source/TableDump.h b/Source/TableDump.h
index 7e1dc5cc..ebc686b2 100644
--- a/Source/TableDump.h
+++ b/Source/TableDump.h
@@ -27,6 +27,13 @@
#import <Cocoa/Cocoa.h>
#import <MCPKit/MCPKit.h>
+typedef enum _SPExportModes {
+ SPExportingSQL = 0,
+ SPExportingCSV = 1,
+ SPExportingXML = 2,
+ SPExportingDOT = 3
+} SPExportMode;
+
@interface TableDump : NSObject
{
IBOutlet id tableDocumentInstance;
@@ -103,6 +110,7 @@
NSMutableArray *fieldMappingArray;
NSMutableArray *fieldMappingButtonOptions;
NSInteger fieldMappingCurrentRow;
+ NSUInteger exportMode;
NSUserDefaults *prefs;
BOOL progressCancelled;
diff --git a/Source/TableDump.m b/Source/TableDump.m
index b6f71c2c..c835b526 100644
--- a/Source/TableDump.m
+++ b/Source/TableDump.m
@@ -45,38 +45,42 @@
#pragma mark IBAction methods
/**
- * Get the tables in db
+ * Update the table lists with the list of tables, retrieved from the
+ * tablesList. If the user has pressed the reload button, trigger a reload
+ * from the server; otherwise used the cached lists.
+ * Retrieve only tables for all modes except SQL.
*/
- (IBAction)reloadTables:(id)sender
{
- MCPResult *queryResult;
- NSInteger i;
-
- //get tables
+
+ // Trigger a reload if necessary
+ if (sender != self) [tablesListInstance updateTables:self];
+
+ // Clear all existing tables
[tables removeAllObjects];
- queryResult = (MCPResult *)[mySQLConnection listTables];
-
- if ([queryResult numOfRows]) [queryResult dataSeek:0];
- NSMutableArray *unsortedTables = [NSMutableArray array];
- for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) {
- [unsortedTables addObject:[[queryResult fetchRowAsArray] objectAtIndex:0]];
+
+ // For all modes, retrieve table and view names
+ NSArray *tablesAndViews = [tablesListInstance allTableAndViewNames];
+ for (id itemName in tablesAndViews) {
+ [tables addObject:[NSMutableArray arrayWithObjects:[NSNumber numberWithBool:YES], itemName, [NSNumber numberWithInt:SP_TABLETYPE_TABLE], nil]];
}
-
- NSSortDescriptor *desc = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
- NSArray *sortedTables = [unsortedTables sortedArrayUsingDescriptors:[NSArray arrayWithObject:desc]];
- [desc release];
-
- for ( i = 0 ; i < [sortedTables count]; i++ ) {
- [tables addObject:
- [NSMutableArray arrayWithObjects:
- [NSNumber numberWithBool:YES],
- [sortedTables objectAtIndex:i],
- nil]];
+
+ // For SQL only, add procedures and functions
+ if (exportMode == SPExportingSQL) {
+ NSArray *procedures = [tablesListInstance allProcedureNames];
+ for (id procName in procedures) {
+ [tables addObject:[NSMutableArray arrayWithObjects:[NSNumber numberWithBool:YES], procName, [NSNumber numberWithInt:SP_TABLETYPE_PROC], nil]];
+ }
+ NSArray *functions = [tablesListInstance allFunctionNames];
+ for (id funcName in functions) {
+ [tables addObject:[NSMutableArray arrayWithObjects:[NSNumber numberWithBool:YES], funcName, [NSNumber numberWithInt:SP_TABLETYPE_FUNC], nil]];
+ }
}
-
- [exportDumpTableView reloadData];
- [exportMultipleCSVTableView reloadData];
- [exportMultipleXMLTableView reloadData];
+
+ // Update interface
+ if (exportMode == SPExportingSQL) [exportDumpTableView reloadData];
+ else if (exportMode == SPExportingCSV) [exportMultipleCSVTableView reloadData];
+ else if (exportMode == SPExportingXML) [exportMultipleXMLTableView reloadData];
}
/**
@@ -160,6 +164,7 @@
switch ( tag ) {
case 5:
// export dump
+ exportMode = SPExportingSQL;
[self reloadTables:self];
file = [NSString stringWithFormat:@"%@_%@.sql", [tableDocumentInstance database], currentDate];
[savePanel setRequiredFileType:@"sql"];
@@ -169,6 +174,7 @@
// Export the full resultset for the currently selected table to a file in CSV format
case 6:
+ exportMode = SPExportingCSV;
file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance table]];
[savePanel setAccessoryView:exportCSVView];
[csvFullStreamingSwitch setEnabled:YES];
@@ -177,12 +183,14 @@
// Export the full resultset for the currently selected table to a file in XML format
case 7:
+ exportMode = SPExportingXML;
file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance table]];
contextInfo = @"exportTableContentAsXML";
break;
// Export the current "browse" view to a file in CSV format
case 8:
+ exportMode = SPExportingCSV;
file = [NSString stringWithFormat:@"%@ view.csv", [tableDocumentInstance table]];
[savePanel setAccessoryView:exportCSVView];
[csvFullStreamingSwitch setEnabled:NO];
@@ -191,12 +199,14 @@
// Export the current "browse" view to a file in XML format
case 9:
+ exportMode = SPExportingXML;
file = [NSString stringWithFormat:@"%@ view.xml", [tableDocumentInstance table]];
contextInfo = @"exportBrowseViewAsXML";
break;
// Export the current custom query result set to a file in CSV format
case 10:
+ exportMode = SPExportingCSV;
file = @"customresult.csv";
[savePanel setAccessoryView:exportCSVView];
[csvFullStreamingSwitch setEnabled:NO];
@@ -205,12 +215,14 @@
// Export the current custom query result set to a file in XML format
case 11:
+ exportMode = SPExportingXML;
file = @"customresult.xml";
contextInfo = @"exportCustomResultAsXML";
break;
// Export multiple tables to a file in CSV format
case 12:
+ exportMode = SPExportingCSV;
[self reloadTables:self];
file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance database]];
[savePanel setAccessoryView:exportMultipleCSVView];
@@ -219,6 +231,7 @@
// Export multiple tables to a file in XML format
case 13:
+ exportMode = SPExportingXML;
[self reloadTables:self];
file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance database]];
[savePanel setAccessoryView:exportMultipleXMLView];
@@ -227,6 +240,7 @@
// graphviz dot file
case 14:
+ exportMode = SPExportingDOT;
[self reloadTables:self];
file = [NSString stringWithString:[tableDocumentInstance database]];
[savePanel setRequiredFileType:@"dot"];
@@ -606,6 +620,7 @@
// Read in the file in a loop
sqlParser = [[SPSQLParser alloc] init];
+ [sqlParser setDelimiterSupport:YES];
sqlDataBuffer = [[NSMutableData alloc] init];
importPool = [[NSAutoreleasePool alloc] init];
while (1) {
@@ -1341,6 +1356,8 @@
NSArray *fieldNames;
NSArray *theRow;
NSMutableArray *selectedTables = [NSMutableArray array];
+ NSMutableArray *selectedProcs = [NSMutableArray array];
+ NSMutableArray *selectedFuncs = [NSMutableArray array];
NSMutableDictionary *viewSyntaxes = [NSMutableDictionary dictionary];
NSMutableString *metaString = [NSMutableString string];
NSMutableString *cellValue = [NSMutableString string];
@@ -1373,10 +1390,22 @@
[tableDocumentInstance setQueryMode:SPImportExportQueryMode];
- // Copy over the selected table names into a table in preparation for iteration
+ // Copy over the selected item names into tables in preparation for iteration
+ NSMutableArray *targetArray;
for ( i = 0 ; i < [tables count] ; i++ ) {
if ( [NSArrayObjectAtIndex(NSArrayObjectAtIndex(tables, i), 0) boolValue] ) {
- [selectedTables addObject:[NSString stringWithString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(tables, i), 1)]];
+ switch ([NSArrayObjectAtIndex(NSArrayObjectAtIndex(tables, i), 2) intValue]) {
+ case SP_TABLETYPE_PROC:
+ targetArray = selectedProcs;
+ break;
+ case SP_TABLETYPE_FUNC:
+ targetArray = selectedFuncs;
+ break;
+ default:
+ targetArray = selectedTables;
+ break;
+ }
+ [targetArray addObject:[NSString stringWithString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(tables, i), 1)]];
}
}
@@ -1675,8 +1704,26 @@
// Add an additional separator between tables
[fileHandle writeData:[[NSString stringWithString:@"\n\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
-
- for (NSString *procedureType in [NSArray arrayWithObjects:@"PROCEDURE", @"FUNCTION", nil]) {
+
+ // Process any deferred views, adding commands to delete the placeholder tables and add the actual views
+ viewSyntaxEnumerator = [viewSyntaxes keyEnumerator];
+ while (tableName = [viewSyntaxEnumerator nextObject]) {
+ [metaString setString:@"\n\n"];
+ [metaString appendFormat:@"DROP TABLE %@;\n", [tableName backtickQuotedString]];
+ [metaString appendFormat:@"%@;\n", [viewSyntaxes objectForKey:tableName]];
+ [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+
+ // Export procedures and functions
+ for (NSString *procedureType in [NSArray arrayWithObjects:@"PROCEDURE", @"FUNCTION", nil]) {
+
+ // Retrieve the array of selected procedures or functions, and skip export if not selected
+ NSMutableArray *selectedItems;
+ if ([procedureType isEqualToString:@"PROCEDURE"]) selectedItems = selectedProcs;
+ else selectedItems = selectedFuncs;
+ if (![selectedItems count]) continue;
+
+ // Retrieve the definitions
queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"/*!50003 SHOW %@ STATUS WHERE `Db` = %@ */;",
procedureType,
[[tableDocumentInstance database] tickQuotedString]]];
@@ -1689,14 +1736,24 @@
[[tableDocumentInstance database] tickQuotedString]]];
[metaString appendString:@"--\n"];
[metaString appendString:@"DELIMITER ;;\n"];
-
+
+ // Loop through the definitions, exporting if enabled
for (int t=0; t<[queryResult numOfRows]; t++) {
NSDictionary *proceduresList = [[NSDictionary alloc] initWithDictionary:[queryResult fetchRowAsDictionary]];
NSString *procedureName = [NSString stringWithFormat:@"%@", [proceduresList objectForKey:@"Name"]];
+
+ // Only proceed if the item was selected for export
+ if (![selectedItems containsObject:procedureName]) continue;
+
+ // Add the "drop" command if specified in the export dialog
+ if ([addDropTableSwitch state] == NSOnState) {
+ [metaString appendString:[NSString stringWithFormat:@"/*!50003 DROP %@ IF EXISTS %@ */;;\n",
+ procedureType,
+ [procedureName backtickQuotedString]]];
+ }
- [metaString appendString:[NSString stringWithFormat:@"/*!50003 DROP %@ IF EXISTS %@ */;;\n",
- procedureType,
- [procedureName backtickQuotedString]]];
+ // Only continue if the "create syntax" is specified in the export dialog
+ if ([addCreateTableSwitch state] == NSOffState) continue;
//Definer is user@host but we need to escape it to `user`@`host`
NSArray *procedureDefiner = [[proceduresList objectForKey:@"Definer"] componentsSeparatedByString:@"@"];
@@ -1748,15 +1805,6 @@
}
- // Process any deferred views, adding commands to delete the placeholder tables and add the actual views
- viewSyntaxEnumerator = [viewSyntaxes keyEnumerator];
- while (tableName = [viewSyntaxEnumerator nextObject]) {
- [metaString setString:@"\n\n"];
- [metaString appendFormat:@"DROP TABLE %@;\n", [tableName backtickQuotedString]];
- [metaString appendFormat:@"%@;\n", [viewSyntaxes objectForKey:tableName]];
- [fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]];
- }
-
// Restore unique checks, foreign key checks, and other settings saved at the start
[metaString setString:@"\n\n\n"];
[metaString appendString:@"/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n"];
diff --git a/Source/TablesList.m b/Source/TablesList.m
index cff345fc..a81c6d75 100644
--- a/Source/TablesList.m
+++ b/Source/TablesList.m
@@ -1750,6 +1750,7 @@
- (void)removeTable
{
NSIndexSet *indexes = [tablesListView selectedRowIndexes];
+ [tablesListView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
// get last index
NSUInteger currentIndex = [indexes lastIndex];