Skip to content

Commit

Permalink
Fixed parsing issue when using trigger.
Browse files Browse the repository at this point in the history
Fixed issue when creation a function on iOS 4.3.
  • Loading branch information
Alexandre Laborie committed Apr 16, 2012
1 parent f7f251e commit 1d13b04
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 29 deletions.
75 changes: 75 additions & 0 deletions Resources/dump_movie_trigger.sql
@@ -0,0 +1,75 @@
PRAGMA foreign_keys=OFF;

------------
-- MOVIES --
------------

-- TABLE movies
-- This table contains a lit of movies.
CREATE TABLE movies(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
rating INTEGER,
actor_count INTEGER DEFAULT 0);

-- Insert some movies.
INSERT INTO "movies"(id, title, rating) VALUES(1,'The Shawshank Redemption',9.2);
INSERT INTO "movies"(id, title, rating) VALUES(2,'The Godfather',9.2);
INSERT INTO "movies"(id, title, rating) VALUES(3,'The Godfather: Part II',9);
INSERT INTO "movies"(id, title, rating) VALUES(4,'The Good, the Bad and the Ugly',8.9);
INSERT INTO "movies"(id, title, rating) VALUES(5,'Pulp Fiction',8.9);

------------
-- ACTORS --
------------

-- TABLE actors
-- This table contains a lit of actors.
CREATE TABLE actors(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT);

-- Insert some actors.
INSERT INTO "actors" VALUES(1,'Morgan Freeman');
INSERT INTO "actors" VALUES(2,'Tim Robbins');
INSERT INTO "actors" VALUES(3,'Marlon Brando');
INSERT INTO "actors" VALUES(4,'Al Pacino');
INSERT INTO "actors" VALUES(5,'Robert Duvall');
INSERT INTO "actors" VALUES(6,'Eli Wallach');
INSERT INTO "actors" VALUES(7,'Clint Eastwood');
INSERT INTO "actors" VALUES(8,'John Travolta');
INSERT INTO "actors" VALUES(9,'Samuel L. Jackson');

---------
-- ACT --
---------

-- TABLE act
-- This table makes the link between the actors and the movies.
CREATE TABLE act(movie_id INTEGER, actor_id INTEGER);

-- TRIGGER increment_actor_count
-- Increments the number of actors in a movie after inserting a new row in table act.
CREATE TRIGGER IF NOT EXISTS increment_actor_count AFTER INSERT ON act
BEGIN
UPDATE movies set actor_count = actor_count + 1 WHERE id = new.movie_id;
END;

-- TRIGGER decrement_actor_count
-- Decrements the number of actors in a movie after deleting a row in the table act.
CREATE TRIGGER IF NOT EXISTS decrement_actor_count AFTER DELETE ON act
BEGIN
UPDATE movies set actor_count = actor_count + 1 WHERE id = old.movie_id;
END;

-- Insert some value in the act table.
INSERT INTO "act" VALUES(1,1);
INSERT INTO "act" VALUES(1,2);
INSERT INTO "act" VALUES(2,3);
INSERT INTO "act" VALUES(2,4);
INSERT INTO "act" VALUES(3,4);
INSERT INTO "act" VALUES(3,5);
INSERT INTO "act" VALUES(4,6);
INSERT INTO "act" VALUES(4,7);
INSERT INTO "act" VALUES(5,8);
INSERT INTO "act" VALUES(5,9);
12 changes: 8 additions & 4 deletions SQLiteKit.xcodeproj/project.pbxproj
Expand Up @@ -7,7 +7,6 @@
objects = {

/* Begin PBXBuildFile section */
FA168252151A545600A22FC4 /* SQLDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA179AFE14CF60C9002FB17F /* SQLDatabaseTests.m */; };
FA168253151A545900A22FC4 /* SQLQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA179B1514D1F60C002FB17F /* SQLQueryTests.m */; };
FA168254151A545D00A22FC4 /* SQLRowTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA93395514D705AA00151B4B /* SQLRowTests.m */; };
FA168260151A6E8600A22FC4 /* dump_short.sql in Resources */ = {isa = PBXBuildFile; fileRef = FA16825F151A6E8600A22FC4 /* dump_short.sql */; };
Expand Down Expand Up @@ -38,10 +37,12 @@
FA8A6B05151A94A4008AC3B9 /* dump_short_no_separator.sql in Resources */ = {isa = PBXBuildFile; fileRef = FA8A6B04151A94A4008AC3B9 /* dump_short_no_separator.sql */; };
FA8A6B08151A98B2008AC3B9 /* dump_movie.sql in Resources */ = {isa = PBXBuildFile; fileRef = FA8A6B07151A98B2008AC3B9 /* dump_movie.sql */; };
FA8A6B0B151A98FF008AC3B9 /* SenTestCase+SQLiteKitAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FA8A6B0A151A98FF008AC3B9 /* SenTestCase+SQLiteKitAdditions.m */; };
FA96D962153CA9B800C6706A /* SQLDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA179AFE14CF60C9002FB17F /* SQLDatabaseTests.m */; };
FAA1F9AF148AD5330013D31B /* SQLDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = FAA1F9AD148AD5330013D31B /* SQLDatabase.h */; settings = {ATTRIBUTES = (); }; };
FAA1F9B0148AD5330013D31B /* SQLDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA1F9AE148AD5330013D31B /* SQLDatabase.m */; };
FACBCB2714D8A27B00004567 /* SQLiteKit.h in Headers */ = {isa = PBXBuildFile; fileRef = FACBCB2614D8A27B00004567 /* SQLiteKit.h */; settings = {ATTRIBUTES = (); }; };
FADDB3D7151BC03B00848B6B /* dump_travel_alter.sql in Resources */ = {isa = PBXBuildFile; fileRef = FADDB3D6151BC03B00848B6B /* dump_travel_alter.sql */; };
FAF99BC7153CA17D007C85F2 /* dump_movie_trigger.sql in Resources */ = {isa = PBXBuildFile; fileRef = FAF99BC6153CA17D007C85F2 /* dump_movie_trigger.sql */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -99,6 +100,7 @@
FAC634521538D18300103667 /* SQLFunction+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SQLFunction+Private.h"; sourceTree = "<group>"; };
FACBCB2614D8A27B00004567 /* SQLiteKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteKit.h; sourceTree = "<group>"; };
FADDB3D6151BC03B00848B6B /* dump_travel_alter.sql */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dump_travel_alter.sql; sourceTree = "<group>"; };
FAF99BC6153CA17D007C85F2 /* dump_movie_trigger.sql */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dump_movie_trigger.sql; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -188,6 +190,7 @@
FA168268151A876400A22FC4 /* dump_travel.sql */,
FADDB3D6151BC03B00848B6B /* dump_travel_alter.sql */,
FA8A6B07151A98B2008AC3B9 /* dump_movie.sql */,
FAF99BC6153CA17D007C85F2 /* dump_movie_trigger.sql */,
);
path = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -312,6 +315,7 @@
FA8A6B05151A94A4008AC3B9 /* dump_short_no_separator.sql in Resources */,
FA8A6B08151A98B2008AC3B9 /* dump_movie.sql in Resources */,
FADDB3D7151BC03B00848B6B /* dump_travel_alter.sql in Resources */,
FAF99BC7153CA17D007C85F2 /* dump_movie_trigger.sql in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -339,11 +343,11 @@
buildActionMask = 2147483647;
files = (
FA256AE315192D91005B3176 /* SQLFileTests.m in Sources */,
FA168252151A545600A22FC4 /* SQLDatabaseTests.m in Sources */,
FA168253151A545900A22FC4 /* SQLQueryTests.m in Sources */,
FA168254151A545D00A22FC4 /* SQLRowTests.m in Sources */,
FA8A6B0B151A98FF008AC3B9 /* SenTestCase+SQLiteKitAdditions.m in Sources */,
FA3B3BA61537953B00A6E744 /* SQLFunctionTests.m in Sources */,
FA96D962153CA9B800C6706A /* SQLDatabaseTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -454,7 +458,7 @@
DSTROOT = /tmp/SQLiteKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Resources/SQLiteKit.pch;
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -468,7 +472,7 @@
DSTROOT = /tmp/SQLiteKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Resources/SQLiteKit.pch;
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
7 changes: 7 additions & 0 deletions SQLiteKit/SQLDatabase.h
Expand Up @@ -133,7 +133,14 @@ typedef NSUInteger SQLDatabaseErrors;

#pragma mark -

/**
@warning On iOS 4.3, the given function is not retained!
*/
- (BOOL)addFunction:(SQLFunction *)function withName:(NSString *)name encoding:(NSInteger)encoding context:(id)object error:(NSError **)error __attribute__ ((nonnull(1, 2)));

/**
@warning On iOS 4.3, the given function is not released!
*/
- (BOOL)removeFunction:(SQLFunction *)function withName:(NSString *)name encoding:(NSInteger)encoding error:(NSError **)error __attribute__ ((nonnull(1, 2)));

#pragma mark -
Expand Down
42 changes: 27 additions & 15 deletions SQLiteKit/SQLDatabase.m
Expand Up @@ -23,6 +23,9 @@
NSString * const kSQLDatabaseCommitNotification = @"@commit";
NSString * const kSQLDatabaseRollbackNotification = @"@rollback";

////////////////////////////////////////////////////////////////////////////////
#pragma mark -

void sqldatabase_function_destroy(void *ptr);

void sqldatabase_function_destroy(void *ptr)
Expand All @@ -33,23 +36,12 @@ void sqldatabase_function_destroy(void *ptr)
}

////////////////////////////////////////////////////////////////////////////////
#pragma mark -

void sqldatabase_update_hook(void *object, int type, char const *database, char const *table, sqlite3_int64 rowID);
int sqldatabase_commit_hook(void *object);
void sqldatabase_rollback_hook(void *object);

@interface SQLDatabase ()

@property (nonatomic, readonly) NSCache *statementsCache;

@property (atomic, retain, readwrite) NSNotificationCenter *notificationCenter;

- (NSString *)_humanReadableStringWithBytes:(int)numberOfBytes;

@end

////////////////////////////////////////////////////////////////////////////////

void sqldatabase_update_hook(void *object, int type, char const *databaseName, char const *tableName, sqlite3_int64 rowID)
{
SQLDatabase *database = (SQLDatabase *)object;
Expand Down Expand Up @@ -111,6 +103,17 @@ void sqldatabase_rollback_hook(void *object)
}

////////////////////////////////////////////////////////////////////////////////
#pragma mark -

@interface SQLDatabase ()

@property (nonatomic, readonly) NSCache *statementsCache;

@property (atomic, retain, readwrite) NSNotificationCenter *notificationCenter;

- (NSString *)_humanReadableStringWithBytes:(int)numberOfBytes;

@end

@implementation SQLDatabase

Expand Down Expand Up @@ -483,10 +486,15 @@ - (BOOL)addFunction:(SQLFunction *)function withName:(NSString *)name encoding:(
NSParameterAssert(function);
NSParameterAssert(name);

[function retain];
function.context = object;
if ( sqlite3_create_function_v2(self.connectionHandle, [name UTF8String], function.numberOfArguments, encoding, function, function.function, function.step, function.final, &sqldatabase_function_destroy) == SQLITE_OK )
#if SQLITE_VERSION_NUMBER >= 3007003
if ( sqlite3_create_function_v2(self.connectionHandle, [name UTF8String], function.numberOfArguments, SQLITE_UTF8, function, function.function, function.step, function.final, &sqldatabase_function_destroy) == SQLITE_OK )
{
[function retain];
#else
if ( sqlite3_create_function(self.connectionHandle, [name UTF8String], function.numberOfArguments, SQLITE_UTF8, function, function.function, function.step, function.final) == SQLITE_OK )
{
#endif
function.context = object;
sqlitekit_verbose(@"Custom function '%@' has been created successfully.", name);
return YES;
}
Expand All @@ -507,7 +515,11 @@ - (BOOL)removeFunction:(SQLFunction *)function withName:(NSString *)name encodin

NSParameterAssert(name);

#if SQLITE_VERSION_NUMBER >= 3007003
if ( sqlite3_create_function_v2(self.connectionHandle, [name UTF8String], function.numberOfArguments, encoding, function, NULL, NULL, NULL, NULL) == SQLITE_OK )
#else
if ( sqlite3_create_function(self.connectionHandle, [name UTF8String], function.numberOfArguments, encoding, function, function.function, function.step, function.final) == SQLITE_OK )
#endif
{
sqlitekit_verbose(@"Custom function '%@' has been removed successfully.", name);
return YES;
Expand Down
68 changes: 58 additions & 10 deletions SQLiteKit/SQLFile.m
Expand Up @@ -116,35 +116,54 @@ char buffer_char_at_index(buffer_t buffer, unsigned int index)
////////////////////////////////////////////////////////////////////////////////
#pragma mark -

unsigned int getNextLine(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffer, unsigned int startingIndex);
bool parser_is_separator_char(char c);
bool parser_read_next_chunk(FILE *stream, buffer_t streamBuffer);
unsigned int parser_get_next_request(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffer, unsigned int startingIndex);

////////////////////////////////////////////////////////////////////////////////
#pragma mark -

unsigned int getNextLine(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffer, unsigned int startingIndex)
bool parser_is_separator_char(char c)
{
return c == ' ' || c == '\t' || c == '\n';
}

bool parser_read_next_chunk(FILE *stream, buffer_t streamBuffer)
{
streamBuffer->length = fread(streamBuffer->data, sizeof(*(streamBuffer->data)), streamBuffer->size, stream);
if ( streamBuffer->length == 0 )
{
return false;
}
return true;
}

unsigned int parser_get_next_request(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffer, unsigned int startingIndex)
{
NSCParameterAssert(stream);
NSCParameterAssert(streamBuffer);
NSCParameterAssert(lineBuffer);

bool endOfLine = false;
unsigned int index = startingIndex;
unsigned int semicolonExpected = 1;

char currentChar = 0;
char previousChar = 0;

char inhibitor = 0;
bool shouldInhibitNextChar = false;
bool shouldCopy = true;
bool hasValidBeginPrefix = false;
bool shouldCheckForBeginPrefix = false;

while ( true )
{
// Head, primary checks before processing.
previousChar = currentChar;
// Can we read another char?
if ( index >= streamBuffer->length )
{
streamBuffer->length = fread(streamBuffer->data, sizeof(*(streamBuffer->data)), streamBuffer->size, stream);
if ( streamBuffer->length == 0 )
if ( parser_read_next_chunk(stream, streamBuffer) == false )
{
return 0;
}
Expand Down Expand Up @@ -201,11 +220,19 @@ unsigned int getNextLine(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffe
case '\t':
case '\r':
{
if ( hasValidBeginPrefix == true )
{
semicolonExpected = 2;
}
index++;
continue;
}
case ' ':
{
if ( hasValidBeginPrefix == true )
{
semicolonExpected = 2;
}
if ( lineBuffer->length == 0 || lineBuffer->data[lineBuffer->length - 1] == ' ' )
{
index++;
Expand Down Expand Up @@ -235,22 +262,43 @@ unsigned int getNextLine(FILE *stream, buffer_t streamBuffer, buffer_t lineBuffe
{
if ( inhibitor == 0 )
{
if ( lineBuffer->length > 0 && lineBuffer->data[lineBuffer->length - 1] == ' ' )
semicolonExpected--;
if ( semicolonExpected == 0 )
{
lineBuffer->length--;
if ( lineBuffer->length > 0 && lineBuffer->data[lineBuffer->length - 1] == ' ' )
{
lineBuffer->length--;
}
}
endOfLine = true;
}
break;
}
case 'n':
case 'N':
{
shouldCheckForBeginPrefix = true;
break;
}
}

// Tail, final treatment.
if ( shouldCopy == true )
{
buffer_append_char(lineBuffer, currentChar);
}
if ( endOfLine == true )
hasValidBeginPrefix = false;
if ( shouldCheckForBeginPrefix == true )
{
shouldCheckForBeginPrefix = false;
if ( inhibitor == 0 && lineBuffer->length > 5 )
{
BOOL hasWordPrefix = ( strncasecmp(&lineBuffer->data[lineBuffer->length - 5], "begin", 5) == 0 );
BOOL hasSeparatorBeforePrefix = parser_is_separator_char(lineBuffer->data[lineBuffer->length - 6]);

hasValidBeginPrefix = hasWordPrefix && hasSeparatorBeforePrefix;
}
}
if ( semicolonExpected == 0 )
{
break;
}
Expand Down Expand Up @@ -374,7 +422,7 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state object
state->itemsPtr = objectsBuffer;
while ( (self.streamBuffer->length > 0 || feof(self.stream) == 0) && numberOfNewObject < length )
{
self.streamBufferStartingIndex = getNextLine(self.stream, self.streamBuffer, self.lineBuffer, self.streamBufferStartingIndex);
self.streamBufferStartingIndex = parser_get_next_request(self.stream, self.streamBuffer, self.lineBuffer, self.streamBufferStartingIndex);
if ( self.lineBuffer->length > 0 )
{
objectsBuffer[numberOfNewObject] = [[[NSString alloc] initWithBytes:self.lineBuffer->data length:self.lineBuffer->length encoding:NSUTF8StringEncoding] autorelease];
Expand Down

0 comments on commit 1d13b04

Please sign in to comment.