Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ccgus/fmdb.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAugust "Gus" Mueller <gus@flyingmeat.com>2014-04-16 21:42:45 +0400
committerAugust "Gus" Mueller <gus@flyingmeat.com>2014-04-16 21:42:45 +0400
commit8941bf15a4c2e16e1850af59e735267bc154d943 (patch)
treee8e102c051a0554612a23bc9c943c00b1c5f2c90
parent3691599e49f6f6ca35dd18d3bc92890e42fc8318 (diff)
parenta29e7c46888ea51c713daf74b3ab037c33d2d0f3 (diff)
Merge pull request #250 from OpenFibers/master
Some commits for FMSQLStatementSplitter
-rw-r--r--README.markdown18
-rw-r--r--src/extra/FMStatementSplitter/FMSQLStatementSplitter.h19
-rw-r--r--src/extra/FMStatementSplitter/FMStatementKeywordRecogniser.m4
-rw-r--r--src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.h14
-rw-r--r--src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.m63
-rw-r--r--src/extra/FMStatementSplitter/FMStatementTokenRecogniser.h12
-rw-r--r--src/sample/main.m35
7 files changed, 102 insertions, 63 deletions
diff --git a/README.markdown b/README.markdown
index 6d5b85d..433d6d2 100644
--- a/README.markdown
+++ b/README.markdown
@@ -109,6 +109,24 @@ When you have finished executing queries and updates on the database, you should
`FMDatabase` can begin and commit a transaction by invoking one of the appropriate methods or executing a begin/end transaction statement.
+### Split Batch Statement
+
+`FMSQLStatementSplitter` can split batch sql statement into several separated statements, then `[FMDatabase executeUpdate:]` or other methods can be used to execute each separated statement:
+
+```
+NSString *batchStatement = @"insert into ftest values ('hello;');"
+ @"insert into ftest values ('hi;');"
+ @"insert into ftest values ('not h!\\\\');"
+ @"insert into ftest values ('definitely not h!')";
+NSArray *statements = [[FMSQLStatementSplitter sharedInstance] statementsFromBatchSqlStatement:batchStatement];
+[queue inDatabase:^(FMDatabase *adb) {
+ for (FMSplittedStatement *sqlittedStatement in statements)
+ {
+ [adb executeUpdate:sqlittedStatement.statementString];
+ }
+}];
+```
+
### Data Sanitization
When providing a SQL statement to FMDB, you should not attempt to "sanitize" any values before insertion. Instead, you should use the standard SQLite binding syntax:
diff --git a/src/extra/FMStatementSplitter/FMSQLStatementSplitter.h b/src/extra/FMStatementSplitter/FMSQLStatementSplitter.h
index d9beba1..e1042f2 100644
--- a/src/extra/FMStatementSplitter/FMSQLStatementSplitter.h
+++ b/src/extra/FMStatementSplitter/FMSQLStatementSplitter.h
@@ -8,14 +8,33 @@
#import <Foundation/Foundation.h>
+/**
+ * The FMSplittedStatement class contains a separated statement.
+ */
@interface FMSplittedStatement : NSObject
+
+/**
+ * Separated statement string.
+ */
@property (nonatomic, retain) NSString *statementString;//statement string
@end
@interface FMSQLStatementSplitter : NSObject
+/**
+ * Get singleton instance.
+ */
+ (instancetype)sharedInstance;
+/**
+ * Split batch sql statement into separated statements.
+ *
+ * @param batchStatement The batch statement string to split.
+ *
+ * @return Returns the array of splitted statements. Each member of return value is an `FMSplittedStatement`.
+ *
+ * @see FMSplittedStatement
+ */
- (NSArray *)statementsFromBatchSqlStatement:(NSString *)batchStatement;
@end
diff --git a/src/extra/FMStatementSplitter/FMStatementKeywordRecogniser.m b/src/extra/FMStatementSplitter/FMStatementKeywordRecogniser.m
index 3ca3c27..b968708 100644
--- a/src/extra/FMStatementSplitter/FMStatementKeywordRecogniser.m
+++ b/src/extra/FMStatementSplitter/FMStatementKeywordRecogniser.m
@@ -98,11 +98,11 @@
NSUInteger remainingChars = [[scanner string] length] - *tokenPosition;
if (remainingChars >= kwLength)
{
- if (CFStringFindWithOptions((CFStringRef)[scanner string], (CFStringRef)keyword, CFRangeMake(*tokenPosition, kwLength), kCFCompareAnchored | kCFCompareCaseInsensitive, NULL))
+ if (CFStringFindWithOptions((CFStringRef)[scanner string], (CFStringRef)keyword, CFRangeMake((CFIndex)*tokenPosition, (CFIndex)kwLength), kCFCompareAnchored | kCFCompareCaseInsensitive, NULL))
{
if (remainingChars == kwLength ||
nil == self.invalidFollowingCharacters ||
- !CFStringFindCharacterFromSet((CFStringRef)[scanner string], (CFCharacterSetRef)self.invalidFollowingCharacters, CFRangeMake(*tokenPosition + kwLength, 1), kCFCompareAnchored, NULL))
+ !CFStringFindCharacterFromSet((CFStringRef)[scanner string], (CFCharacterSetRef)self.invalidFollowingCharacters, CFRangeMake((CFIndex)(*tokenPosition + kwLength), 1), kCFCompareAnchored, NULL))
{
NSRange result = NSMakeRange(*tokenPosition, kwLength);
*tokenPosition = *tokenPosition + kwLength;
diff --git a/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.h b/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.h
index 567e251..5b5f400 100644
--- a/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.h
+++ b/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.h
@@ -91,40 +91,40 @@
*
* @see endQuote
*/
-@property (readwrite,copy) NSString *startQuote;
+@property (readwrite,nonatomic,copy) NSString *startQuote;
/**
* Determines the string used to indicate the end of the quoted literal.
*
* @see startQuote
*/
-@property (readwrite,copy) NSString *endQuote;
+@property (readwrite,nonatomic,copy) NSString *endQuote;
/**
* Determines the string used to indicate an escaped character in the quoted literal.
*/
-@property (readwrite,copy) NSString *escapeSequence;
+@property (readwrite,nonatomic,copy) NSString *escapeSequence;
/**
* If `YES`, quoted string will contains `escapeSequence`.
* If `NO`, quoted string will not contains `escapeSequence`.
* Default is `NO`.
*/
-@property (nonatomic, assign) BOOL shouldQuoteEscapeSequence;
+@property (nonatomic,assign) BOOL shouldQuoteEscapeSequence;
/**
* Determines how much of the input string to consume when an escaped literal is found, and what to replace it with.
*/
-@property (readwrite,copy) NSString *(^escapeReplacer)(NSString *tokenStream, NSUInteger *quotePosition);
+@property (readwrite,nonatomic,copy) NSString *(^escapeReplacer)(NSString *tokenStream, NSUInteger *quotePosition);
/**
* Determines the maximum length of the quoted literal not including quotes. To indicate the literal can be any length specify NSNotFound.
*/
-@property (readwrite,assign) NSUInteger maximumLength;
+@property (readwrite,nonatomic,assign) NSUInteger maximumLength;
/**
* Determines the name of the token produced.
*/
-@property (readwrite,copy) NSString *name;
+@property (readwrite,nonatomic,copy) NSString *name;
@end
diff --git a/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.m b/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.m
index 3db01d6..f27a08a 100644
--- a/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.m
+++ b/src/extra/FMStatementSplitter/FMStatementQuotedRecogniser.m
@@ -54,49 +54,6 @@
return self;
}
-#define CPQuotedRecogniserStartQuoteKey @"Q.s"
-#define CPQuotedRecogniserEndQuoteKey @"Q.e"
-#define CPQuotedRecogniserEscapeSequenceKey @"Q.es"
-#define CPQuotedRecogniserMaximumLengthKey @"Q.m"
-#define CPQuotedRecogniserNameKey @"Q.n"
-
-- (id)initWithCoder:(NSCoder *)aDecoder
-{
- self = [super init];
-
- if (nil != self)
- {
- [self setStartQuote:[aDecoder decodeObjectForKey:CPQuotedRecogniserStartQuoteKey]];
- [self setEndQuote:[aDecoder decodeObjectForKey:CPQuotedRecogniserEndQuoteKey]];
- [self setEscapeSequence:[aDecoder decodeObjectForKey:CPQuotedRecogniserEscapeSequenceKey]];
- @try
- {
- [self setMaximumLength:[aDecoder decodeIntegerForKey:CPQuotedRecogniserMaximumLengthKey]];
- }
- @catch (NSException *exception)
- {
- NSLog(@"Warning, value for maximum length too long for this platform, allowing infinite lengths");
- [self setMaximumLength:NSNotFound];
- }
- [self setName:[aDecoder decodeObjectForKey:CPQuotedRecogniserNameKey]];
- }
-
- return self;
-}
-
-- (void)encodeWithCoder:(NSCoder *)aCoder
-{
- if (nil != [self escapeReplacer])
- {
- NSLog(@"Warning: encoding CPQuoteRecogniser with an escapeReplacer set. This will not be recreated when decoded.");
- }
- [aCoder encodeObject:[self startQuote] forKey:CPQuotedRecogniserStartQuoteKey];
- [aCoder encodeObject:[self endQuote] forKey:CPQuotedRecogniserEndQuoteKey];
- [aCoder encodeObject:[self escapeSequence] forKey:CPQuotedRecogniserEscapeSequenceKey];
- [aCoder encodeInteger:[self maximumLength] forKey:CPQuotedRecogniserMaximumLengthKey];
- [aCoder encodeObject:[self name] forKey:CPQuotedRecogniserNameKey];
-}
-
- (NSRange)recogniseRangeWithScanner:(NSScanner *)scanner currentTokenPosition:(NSUInteger *)tokenPosition
{
NSString *(^er)(NSString *tokenStream, NSUInteger *quotePosition) = [self escapeReplacer];
@@ -104,12 +61,12 @@
NSUInteger endQuoteLength = [self.endQuote length];
NSString *tokenString = [scanner string];
- long inputLength = [tokenString length];
+ NSUInteger inputLength = [tokenString length];
NSUInteger rangeLength = [FMStatementQuotedRecogniser minWithLeftParam:inputLength - *tokenPosition
rightParam:startQuoteLength + endQuoteLength + self.maximumLength];
- CFRange searchRange = CFRangeMake(*tokenPosition, rangeLength);
+ CFRange searchRange = CFRangeMake((CFIndex)*tokenPosition, (CFIndex)rangeLength);
CFRange range;
- BOOL matched = CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.startQuote, searchRange, kCFCompareAnchored, &range);
+ Boolean matched = CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.startQuote, searchRange, kCFCompareAnchored, &range);
CFMutableStringRef outputString = CFStringCreateMutable(kCFAllocatorDefault, 0);
@@ -120,22 +77,22 @@
CFRange endRange;
CFRange escapeRange;
- BOOL matchedEndSequence = CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.endQuote, searchRange, 0L, &endRange);
- BOOL matchedEscapeSequence = nil == self.escapeSequence ? NO : CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.escapeSequence, searchRange, 0L, &escapeRange);
+ Boolean matchedEndSequence = CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.endQuote, searchRange, 0L, &endRange);
+ Boolean matchedEscapeSequence = nil == self.escapeSequence ? NO : CFStringFindWithOptions((CFStringRef)tokenString, (CFStringRef)self.escapeSequence, searchRange, 0L, &escapeRange);
- while (matchedEndSequence && searchRange.location < inputLength)
+ while (matchedEndSequence && (NSUInteger)searchRange.location < inputLength)
{
if (!matchedEscapeSequence || endRange.location < escapeRange.location)//End quote is not escaped by escape sequence.
{
NSUInteger resultRangeBegin = *tokenPosition;
- *tokenPosition = endRange.location + endRange.length;
+ *tokenPosition = (NSUInteger)(endRange.location + endRange.length);
NSUInteger resultRangeLength = *tokenPosition - resultRangeBegin;
CFRelease(outputString);
return NSMakeRange(resultRangeBegin, resultRangeLength);
}
else//End quote is escaped by escape sequence
{
- NSUInteger quotedPosition = escapeRange.location + escapeRange.length;
+ NSUInteger quotedPosition = (NSUInteger)(escapeRange.location + escapeRange.length);
CFRange subStrRange = CFRangeMake(searchRange.location,
escapeRange.location + (self.shouldQuoteEscapeSequence ? escapeRange.length : 0) - searchRange.location);
CFStringRef substr = CFStringCreateWithSubstring(kCFAllocatorDefault, (CFStringRef)tokenString, subStrRange);
@@ -158,8 +115,8 @@
CFRelease(substr);
quotedPosition += 1;
}
- searchRange.length = searchRange.location + searchRange.length - quotedPosition;
- searchRange.location = quotedPosition;
+ searchRange.length = searchRange.location + searchRange.length - (CFIndex)quotedPosition;
+ searchRange.location = (CFIndex)quotedPosition;
if (endRange.location < searchRange.location)
{
diff --git a/src/extra/FMStatementSplitter/FMStatementTokenRecogniser.h b/src/extra/FMStatementSplitter/FMStatementTokenRecogniser.h
index be86796..4ae25f9 100644
--- a/src/extra/FMStatementSplitter/FMStatementTokenRecogniser.h
+++ b/src/extra/FMStatementSplitter/FMStatementTokenRecogniser.h
@@ -8,9 +8,19 @@
#import <Foundation/Foundation.h>
-@protocol FMStatementTokenRecogniser <NSObject, NSCoding>
+/**
+ * The FMStatementTokenRecogniser protocol.
+ */
+@protocol FMStatementTokenRecogniser <NSObject>
@required
+/**
+ * Recognise token with a scanner.
+ * @param scanner The recognising scanner.
+ * @param tokenPosition Begining token position to recognise of scanner.
+ *
+ * @return Returns the recognised token range of scanner. If not recognised, the location of return value is `NSNotFound`.
+ */
- (NSRange)recogniseRangeWithScanner:(NSScanner *)scanner currentTokenPosition:(NSUInteger *)tokenPosition;
@end
diff --git a/src/sample/main.m b/src/sample/main.m
index e44b9b2..0922023 100644
--- a/src/sample/main.m
+++ b/src/sample/main.m
@@ -5,6 +5,7 @@
#import <Foundation/Foundation.h>
#import "FMDB.h"
+#import "FMSQLStatementSplitter.h"
#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }
@@ -1078,6 +1079,40 @@ int main (int argc, const char * argv[]) {
}];
+ //Example for splitting batch statement
+ {
+ NSArray *batchStringArray = @[@"insert into ftest values ('hello;');",
+ @"insert into ftest values ('hi;');",
+ @"insert into ftest values ('not h!\\\\');",
+ @"insert into ftest values ('definitely not h!')"];
+ NSMutableString *batchStatement = [NSMutableString string];
+ for (NSString *str in batchStringArray)
+ {
+ [batchStatement appendString:str];
+ }
+ NSArray *statements = [[FMSQLStatementSplitter sharedInstance] statementsFromBatchSqlStatement:batchStatement];
+ NSLog(@"Number of sqlitted statements is %lu (should be %lu)", (unsigned long)statements.count, (unsigned long)batchStringArray.count);
+
+ if (statements.count == batchStringArray.count)
+ {
+ for (NSUInteger splittedIndex = 0; splittedIndex < statements.count; splittedIndex++)
+ {
+ NSString *originalStatement = batchStringArray[splittedIndex];
+ NSString *splittedStatement = ((FMSplittedStatement *)statements[splittedIndex]).statementString;
+ if (![originalStatement isEqualToString:splittedStatement])
+ {
+ NSLog(@"Splitter failed! Original batch string is %@", batchStatement);
+ }
+ }
+ }
+
+ [queue inDatabase:^(FMDatabase *adb) {
+ for (FMSplittedStatement *sqlittedStatement in statements)
+ {
+ [adb executeUpdate:sqlittedStatement.statementString];
+ }
+ }];
+ }
NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);