Skip to content

Commit

Permalink
Apparently "pragma synchronous" defaults to "full", and not "normal" …
Browse files Browse the repository at this point in the history
…as the documentation states. It also appears this needs to be set per-connection. None of this is very well documented in sqlite.
  • Loading branch information
robbiehanson committed Aug 5, 2014
1 parent dfc4782 commit 59b79ae
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 31 deletions.
3 changes: 2 additions & 1 deletion Testing/Benchmarking/BenchmarkYapDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ + (void)runTestsWithCompletion:(dispatch_block_t)completionBlock

// Create database
YapDatabaseOptions *options = [[YapDatabaseOptions alloc] init];
// options.pragmaSynchronous = YapDatabasePragmaSynchronous_Off; // Uncomment for full speed
// options.pragmaSynchronous = YapDatabasePragmaSynchronous_Normal; // Use for faster speed
// options.pragmaSynchronous = YapDatabasePragmaSynchronous_Off; // Use for fastest speed

database = [[YapDatabase alloc] initWithPath:databasePath
objectSerializer:NULL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,9 @@ - (BOOL)createIfNeeded
#if DEBUG
if ([oldVersionTag isEqualToString:versionTag])
{
__unsafe_unretained YapDatabase *database = databaseTransaction->connection->database;
sqlite3 *db = databaseTransaction->connection->db;

NSDictionary *columns = [database columnNamesAndAffinityForTable:[self tableName] using:db];
NSDictionary *columns = [YapDatabase columnNamesAndAffinityForTable:[self tableName] using:db];

YapDatabaseSecondaryIndexSetup *setup = secondaryIndexConnection->secondaryIndex->setup;

Expand Down
11 changes: 7 additions & 4 deletions YapDatabase/Internal/YapDatabasePrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ extern NSString *const YapDatabaseNotificationKey;
/**
* General utility methods.
**/
- (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb;
- (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb;
- (NSDictionary *)columnNamesAndAffinityForTable:(NSString *)tableName using:(sqlite3 *)aDb;
+ (int)pragma:(NSString *)pragmaSetting using:(sqlite3 *)db;
+ (NSString *)pragmaValueForAutoVacuum:(int)auto_vacuum;
+ (NSString *)pragmaValueForSynchronous:(int)synchronous;
+ (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb;
+ (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb;
+ (NSDictionary *)columnNamesAndAffinityForTable:(NSString *)tableName using:(sqlite3 *)aDb;

/**
* New connections inherit their default values from this structure.
Expand Down Expand Up @@ -160,7 +163,7 @@ extern NSString *const YapDatabaseNotificationKey;
/**
* Configures database encryption via SQLCipher.
**/
- (BOOL)configureEncryptionForDatabase:(sqlite3*)sqlite;
- (BOOL)configureEncryptionForDatabase:(sqlite3 *)sqlite;
#endif

@end
Expand Down
109 changes: 87 additions & 22 deletions YapDatabase/YapDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)in
databasePath = path;
options = inOptions ? [inOptions copy] : [[YapDatabaseOptions alloc] init];

BOOL isNewDatabaseFile = ![[NSFileManager defaultManager] fileExistsAtPath:databasePath];

BOOL(^openConfigCreate)(void) = ^BOOL (void) { @autoreleasepool {

BOOL result = YES;
Expand All @@ -289,7 +291,7 @@ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)in
#ifdef SQLITE_HAS_CODEC
if (result) result = [self configureEncryptionForDatabase:db];
#endif
if (result) result = [self configureDatabase];
if (result) result = [self configureDatabase:isNewDatabaseFile];
if (result) result = [self createTables];

if (!result && db)
Expand Down Expand Up @@ -348,6 +350,7 @@ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)in

if (renamed)
{
isNewDatabaseFile = YES;
result = openConfigCreate();
if (result) {
YDBLogInfo(@"Database corruption resolved. Renamed corrupt file. (newDB=%@) (corruptDB=%@)",
Expand All @@ -368,6 +371,7 @@ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)in

if (deleted)
{
isNewDatabaseFile = YES;
result = openConfigCreate();
if (result) {
YDBLogInfo(@"Database corruption resolved. Deleted corrupt file. (name=%@)",
Expand Down Expand Up @@ -525,33 +529,38 @@ - (BOOL)openDatabase
* Configures the database connection.
* This mainly means enabling WAL mode, and configuring the auto-checkpoint.
**/
- (BOOL)configureDatabase
- (BOOL)configureDatabase:(BOOL)isNewDatabaseFile
{
int status;

// Set mandatory pragmas

status = sqlite3_exec(db, "PRAGMA journal_mode = WAL;", NULL, NULL, NULL);
if (status != SQLITE_OK)
{
YDBLogError(@"Error setting PRAGMA journal_mode: %d %s", status, sqlite3_errmsg(db));
return NO;
}

if (options.pragmaSynchronous == YapDatabasePragmaSynchronous_Off ||
options.pragmaSynchronous == YapDatabasePragmaSynchronous_Full )
// Set synchronous to normal for THIS sqlite instance.
//
// This does NOT affect normal connections.
// That is, this does NOT affect YapDatabaseConnection instances.
// The sqlite connections of normal YapDatabaseConnection instances will follow the set pragmaSynchronous value.
//
// The reason we hardcode normal for this sqlite instance is because
// it's only used to write the initial snapshot value.
// And this doesn't need to be durable, as it is initialized to zero everytime.
//
// (This sqlite db is also used to perform checkpoints.
// But a normal value won't affect these operations,
// as they will perform sync operations whether the connection is normal or full.)

status = sqlite3_exec(db, "PRAGMA synchronous = NORMAL;", NULL, NULL, NULL);
if (status != SQLITE_OK)
{
char *pragma_stmt;

if (options.pragmaSynchronous == YapDatabasePragmaSynchronous_Off)
pragma_stmt = "PRAGMA synchronous = OFF;";
else
pragma_stmt = "PRAGMA synchronous = FULL;";

status = sqlite3_exec(db, pragma_stmt, NULL, NULL, NULL);
if (status != SQLITE_OK)
{
YDBLogError(@"Error setting PRAGMA synchronous: %d %s", status, sqlite3_errmsg(db));
// This isn't critical, so we can continue.
}
YDBLogError(@"Error setting PRAGMA synchronous: %d %s", status, sqlite3_errmsg(db));
// This isn't critical, so we can continue.
}

// Disable autocheckpointing.
Expand All @@ -569,8 +578,8 @@ - (BOOL)configureDatabase
#ifdef SQLITE_HAS_CODEC
/**
* Configures database encryption via SQLCipher.
**/
- (BOOL)configureEncryptionForDatabase:(sqlite3*)sqlite
**/
- (BOOL)configureEncryptionForDatabase:(sqlite3 *)sqlite
{
int status;

Expand Down Expand Up @@ -650,10 +659,64 @@ - (BOOL)createTables
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ (int)pragma:(NSString *)pragmaSetting using:(sqlite3 *)db
{
if (pragmaSetting == nil) return 0;

sqlite3_stmt *statement;
NSString *pragma = [NSString stringWithFormat:@"PRAGMA %@;", pragmaSetting];

int status = sqlite3_prepare_v2(db, [pragma UTF8String], -1, &statement, NULL);
if (status != SQLITE_OK)
{
YDBLogError(@"%@: Error creating statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
return NO;
}

int result = 0;

status = sqlite3_step(statement);
if (status == SQLITE_ROW)
{
result = sqlite3_column_int(statement, 0);
}
else if (status == SQLITE_ERROR)
{
YDBLogError(@"%@: Error executing statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
}

sqlite3_finalize(statement);
statement = NULL;

return result;
}

+ (NSString *)pragmaValueForAutoVacuum:(int)auto_vacuum
{
switch(auto_vacuum)
{
case 0 : return @"NONE";
case 1 : return @"FULL";
case 2 : return @"INCREMENTAL";
default: return @"UNKNOWN";
}
}

+ (NSString *)pragmaValueForSynchronous:(int)synchronous
{
switch(synchronous)
{
case 0 : return @"OFF";
case 1 : return @"NORMAL";
case 2 : return @"FULL";
default: return @"UNKNOWN";
}
}

/**
* Returns whether or not the given table exists.
**/
- (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb
+ (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb
{
if (tableName == nil) return NO;

Expand Down Expand Up @@ -692,7 +755,7 @@ - (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb
/**
* Extracts and returns column names from the given table in the database.
**/
- (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb
+ (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb
{
if (tableName == nil) return nil;

Expand All @@ -710,6 +773,8 @@ - (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb

while ((status = sqlite3_step(statement)) == SQLITE_ROW)
{
// cid|name|type|notnull|dflt|value|pk

const unsigned char *text = sqlite3_column_text(statement, 1);
int textSize = sqlite3_column_bytes(statement, 1);

Expand Down Expand Up @@ -737,7 +802,7 @@ - (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb
*
* key:(NSString *)columnName -> value:(NSString *)affinity
**/
- (NSDictionary *)columnNamesAndAffinityForTable:(NSString *)tableName using:(sqlite3 *)aDb
+ (NSDictionary *)columnNamesAndAffinityForTable:(NSString *)tableName using:(sqlite3 *)aDb
{
if (tableName == nil) return nil;

Expand Down
21 changes: 21 additions & 0 deletions YapDatabase/YapDatabaseConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,27 @@ - (id)initWithDatabase:(YapDatabase *)inDatabase
}
else
{
// Set configurable pragmas

YapDatabasePragmaSynchronous pragmaSynchronous = database.options.pragmaSynchronous;

if (pragmaSynchronous == YapDatabasePragmaSynchronous_Off ||
pragmaSynchronous == YapDatabasePragmaSynchronous_Normal)
{
char *pragma_stmt = NULL;

if (pragmaSynchronous == YapDatabasePragmaSynchronous_Off)
pragma_stmt = "PRAGMA synchronous = OFF;";
else
pragma_stmt = "PRAGMA synchronous = NORMAL;";

status = sqlite3_exec(db, pragma_stmt, NULL, NULL, NULL);
if (status != SQLITE_OK)
{
YDBLogError(@"Error setting PRAGMA synchronous: %d %s", status, sqlite3_errmsg(db));
}
}

// Disable autocheckpointing.
//
// YapDatabase has its own optimized checkpointing algorithm built-in.
Expand Down
2 changes: 1 addition & 1 deletion YapDatabase/YapDatabaseOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ typedef NSString* (^YapDatabaseOptionsPassphraseBlock)(void);
*
* Note that YapDatabase uses sqlite in WAL mode.
*
* The default value is YapDatabasePragmaSynchronous_Normal.
* The default value is YapDatabasePragmaSynchronous_Full.
**/
@property (nonatomic, assign, readwrite) YapDatabasePragmaSynchronous pragmaSynchronous;

Expand Down
2 changes: 1 addition & 1 deletion YapDatabase/YapDatabaseOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ - (id)init
if ((self = [super init]))
{
corruptAction = YapDatabaseCorruptAction_Rename;
pragmaSynchronous = YapDatabasePragmaSynchronous_Normal;
pragmaSynchronous = YapDatabasePragmaSynchronous_Full;
}
return self;
}
Expand Down

0 comments on commit 59b79ae

Please sign in to comment.