Skip to content

Commit

Permalink
Merge branch 'threadtests'
Browse files Browse the repository at this point in the history
Conflicts:
	README.markdown
	src/FMDatabase.m
  • Loading branch information
ccgus committed Feb 15, 2012
2 parents be9cbd1 + ab3b78c commit b2047f9
Show file tree
Hide file tree
Showing 14 changed files with 1,886 additions and 333 deletions.
28 changes: 22 additions & 6 deletions CHANGES_AND_TODO_LIST.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,41 @@ Zip, nada, zilch. Got any ideas?

If you would like to contribute some code- awesome! I just ask that you make it conform to the coding conventions already set in here, and to add a couple of tests for your new code to fmdb.m. And of course, the code should be of general use to more than just a couple of folks. Send your patches to [email protected].

2012.02.10:
Changed up FMDatabasePool so that you can't "pop" it from a pool anymore. I just consider this too risky- use the block based functions instead.
Also provided a good reason in main.m for why you should use FMDatabaseQueue instead. Search for "ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS".
I consider this branch 2.0 at this point- I'll let it bake for a couple of days, then push it to the main repo.


2012.01.06:
Added a new method to FMDatabase to make custom functions out of a block:
- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block

Check out the function "testSQLiteFunction" in main.m for an example.

2011.07.14:
Added methods for named parameters, using keys from an NSDictionary (Thanks to Drarok Ithaqua for the patches!)
Changed FMDatabase's "- (BOOL)update:(NSString*)sql error:(NSError**)outErr bind:(id)bindArgs, ... " to "- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ..." as the previous method didn't actually work as advertised in the way it was written. Thanks to @jaekwon for pointing this out.

2011.06.22
Changed some methods to properties. Hello 2011.
Added a warning when you try and use a database that wasn't opened. Hacked together based on patches from Drarok Ithaqua.
Fixed a problem under GC where leaked statments were keeping a database from closing. Patch from Chris Dolan.
Added + (BOOL)isThreadSafe to FMDatabase. It'll let you know if the version of SQLite you are running is compiled with it's thread safe options. THIS DOES NOT MEAN FMDATABASE IS THREAD SAFE. I haven't done a review of it for this case, so I'm just saying.

2011.04.09
Added a method to validate a SQL statement.
Added a method to retrieve the number of columns in a result set.
Added two methods to execute queries and updates with NSString-style format specifiers.
Added a method to validate a SQL statement.
Added a method to retrieve the number of columns in a result set.
Added two methods to execute queries and updates with NSString-style format specifiers.
Thanks to Dave DeLong for the patches!

2011.03.12
Added compatibility with garbage collection.
When an FMDatabase is closed, all open FMResultSets pertaining to that database are also closed.
Added compatibility with garbage collection.
When an FMDatabase is closed, all open FMResultSets pertaining to that database are also closed.
Added:
- (id) objectForColumnIndex:(int)columnIdx;
- (id) objectForColumnName:(NSString*)columnName;
Changes by Dave DeLong.
Changes by Dave DeLong.

2011.02.05
The -(int)changes; method on FMDatabase is a bit more robust now, and there's a new static library target. And if a database path is nil, we now open up a :memory: database. Patch from Pascal Pfiffner!
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ Pascal Pfiffner
Dave DeLong
Drarok Ithaqua
Chris Dolan
Sriram Patil

Aaaaannnd, Gus Mueller (that's me!)
74 changes: 69 additions & 5 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
# FMDB

This is an Objective-C wrapper around SQLite: http://sqlite.org/

## Mailing List:

## The FMDB Mailing List:
http://groups.google.com/group/fmdb

## Usage
## Read the SQLite FAQ:
http://www.sqlite.org/faq.html

Since FMDB is built on top of SQLite, you're going to want to read this page top to bottom at least once. And while you're there, make sure to bookmark the SQLite Documentation page: http://www.sqlite.org/docs.html

There are two main classes in FMDB:
## Automatic Reference Counting (ARC) or Manual Memory Management?
You can use either style in your Cocoa project. FMDB Will figure out which you are using at compile time and do the right thing.

## Usage
There are three main classes in FMDB:

1. `FMDatabase` - Represents a single SQLite database. Used for executing SQL statements.
2. `FMResultSet` - Represents the results of executing a query on an `FMDatabase`.
3. `FMDatabaseQueue` - If you're wanting to perform queries and updates on multiple threads, you'll want to use this class. It's described in the "Thread Safety" section below.

### Database Creation
An `FMDatabase` is created with a path to a SQLite database file. This path can be one of these three:
Expand Down Expand Up @@ -91,7 +98,16 @@ When providing a SQL statement to FMDB, you should not attempt to "sanitize" any

INSERT INTO myTable VALUES (?, ?, ?)

The `?` character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an `NSArray` or a `va_list`), which are properly escaped for you.
The `?` character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an `NSArray`, `NSDictionary`, or a `va_list`), which are properly escaped for you.

Alternatively, you may use named parameters syntax:

INSERT INTO myTable VALUES (:id, :name, :value)

The parameters *must* start with a colon. SQLite itself supports other characters, but internally the Dictionary keys are prefixed with a colon, do **not** include the colon in your dictionary keys.

NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
[db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withArgumentsInDictionary:argsDict];

Thus, you SHOULD NOT do this (or anything like this):

Expand All @@ -115,6 +131,54 @@ Alternatively, you can use the `-execute*WithFormat:` variant to use `NSString`-

Internally, the `-execute*WithFormat:` methods are properly boxing things for you. The following percent modifiers are recognized: `%@`, `%c`, `%s`, `%d`, `%D`, `%i`, `%u`, `%U`, `%hi`, `%hu`, `%qi`, `%qu`, `%f`, `%g`, `%ld`, `%lu`, `%lld`, and `%llu`. Using a modifier other than those will have unpredictable results. If, for some reason, you need the `%` character to appear in your SQL statement, you should use `%%`.


<h2 id="threads">Using FMDatabaseQueue and Thread Safety.</h2>

Using a single instance of FMDatabase from multiple threads at once is a bad idea. It has always been OK to make a FMDatabase object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. Bad things will eventually happen and you'll eventually get something to crash, or maybe get an exception, or maybe meteorites will fall out of the sky and hit your Mac Pro. *This would suck*.

**So don't instantiate a single FMDatabase object and use it across multiple threads.**

Instead, use FMDatabaseQueue. It's your friend and it's here to help. Here's how to use it:

First, make your queue.

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

Then use it like so:

[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
FMResultSet *rs = [db executeQuery:@"select * from foo"];
while ([rs next]) {
}
}];

An easy way to wrap things up in a transaction can be done like this:

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
if (whoopsSomethingWrongHappened) {
*rollback = YES;
return;
}
// etc…
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}];


FMDatabaseQueue will make a serialized GCD queue in the background and execute the blocks you pass to the GCD queue. This means if you call your FMDatabaseQueue's methods from multiple threads at the same time GDC will execute them in the order they are received. This means queries and updates won't step on each other's toes, and every one is happy.

## Making custom sqlite functions, based on blocks.

You can do this! For an example, look for "makeFunctionNamed:" in main.m

## History

The history and changes are availbe on its [GitHub page](https://github.com/ccgus/fmdb) and are summarized in the "CHANGES_AND_TODO_LIST.txt" file.
Expand Down
42 changes: 34 additions & 8 deletions fmdb.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objectVersion = 46;
objects = {

/* Begin PBXBuildFile section */
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
8DD76F9F0486AA7600D96B5E /* fmdb.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* fmdb.1 */; };
CC47A00F148581E9002CCDAB /* FMDatabaseQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = CC47A00D148581E9002CCDAB /* FMDatabaseQueue.h */; };
CC47A010148581E9002CCDAB /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = CC47A00E148581E9002CCDAB /* FMDatabaseQueue.m */; };
CC47A011148581E9002CCDAB /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = CC47A00E148581E9002CCDAB /* FMDatabaseQueue.m */; };
CC50F2CD0DF9183600E4AAAE /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CC50F2CB0DF9183600E4AAAE /* FMDatabaseAdditions.m */; };
CC9E4EB913B31188005F9210 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9E4EB813B31188005F9210 /* FMDatabasePool.m */; };
CC9E4EBA13B31188005F9210 /* FMDatabasePool.h in Headers */ = {isa = PBXBuildFile; fileRef = CC9E4EB713B31188005F9210 /* FMDatabasePool.h */; };
CC9E4EBB13B31188005F9210 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9E4EB813B31188005F9210 /* FMDatabasePool.m */; };
CCBE26C113B3BA8C006F6C37 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCBE26C013B3BA8C006F6C37 /* AppKit.framework */; };
CCBEBDAC0DF5DE1A003DDD08 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CCBEBDAB0DF5DE1A003DDD08 /* libsqlite3.dylib */; };
CCC24EC10A13E34D00A6D3E3 /* FMDatabase.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CCC24EBA0A13E34D00A6D3E3 /* FMDatabase.h */; };
CCC24EC20A13E34D00A6D3E3 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC24EBB0A13E34D00A6D3E3 /* FMDatabase.m */; };
Expand Down Expand Up @@ -46,11 +53,16 @@
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmdb_Prefix.pch; sourceTree = "<group>"; };
8DD76FA10486AA7600D96B5E /* fmdb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fmdb; sourceTree = BUILT_PRODUCTS_DIR; };
C6859EA3029092ED04C91782 /* fmdb.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fmdb.1; sourceTree = "<group>"; };
CC47A00D148581E9002CCDAB /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabaseQueue.h; path = src/FMDatabaseQueue.h; sourceTree = "<group>"; };
CC47A00E148581E9002CCDAB /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabaseQueue.m; path = src/FMDatabaseQueue.m; sourceTree = "<group>"; };
CC50F2CB0DF9183600E4AAAE /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabaseAdditions.m; path = src/FMDatabaseAdditions.m; sourceTree = "<group>"; };
CC50F2CC0DF9183600E4AAAE /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabaseAdditions.h; path = src/FMDatabaseAdditions.h; sourceTree = "<group>"; };
CC8C138A0E3135C400FBE1E7 /* CHANGES_AND_TODO_LIST.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES_AND_TODO_LIST.txt; sourceTree = "<group>"; };
CC8C138B0E3135C400FBE1E7 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
CC8C138C0E3135C400FBE1E7 /* CONTRIBUTORS.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CONTRIBUTORS.txt; sourceTree = "<group>"; };
CC9E4EB713B31188005F9210 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabasePool.h; path = src/FMDatabasePool.h; sourceTree = "<group>"; };
CC9E4EB813B31188005F9210 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabasePool.m; path = src/FMDatabasePool.m; sourceTree = "<group>"; };
CCBE26C013B3BA8C006F6C37 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
CCBEBDAB0DF5DE1A003DDD08 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = /usr/lib/libsqlite3.dylib; sourceTree = "<absolute>"; };
CCC24EBA0A13E34D00A6D3E3 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabase.h; path = src/FMDatabase.h; sourceTree = "<group>"; };
CCC24EBB0A13E34D00A6D3E3 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabase.m; path = src/FMDatabase.m; sourceTree = "<group>"; };
Expand All @@ -69,6 +81,7 @@
files = (
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
CCBEBDAC0DF5DE1A003DDD08 /* libsqlite3.dylib in Frameworks */,
CCBE26C113B3BA8C006F6C37 /* AppKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -103,12 +116,16 @@
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
CC47A00D148581E9002CCDAB /* FMDatabaseQueue.h */,
CC47A00E148581E9002CCDAB /* FMDatabaseQueue.m */,
CC50F2CC0DF9183600E4AAAE /* FMDatabaseAdditions.h */,
CC50F2CB0DF9183600E4AAAE /* FMDatabaseAdditions.m */,
CCC24EBA0A13E34D00A6D3E3 /* FMDatabase.h */,
CCC24EBB0A13E34D00A6D3E3 /* FMDatabase.m */,
CCC24EBF0A13E34D00A6D3E3 /* FMResultSet.h */,
CCC24EC00A13E34D00A6D3E3 /* FMResultSet.m */,
CC9E4EB713B31188005F9210 /* FMDatabasePool.h */,
CC9E4EB813B31188005F9210 /* FMDatabasePool.m */,
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */,
CCC24EBE0A13E34D00A6D3E3 /* fmdb.m */,
);
Expand All @@ -120,6 +137,7 @@
children = (
CCBEBDAB0DF5DE1A003DDD08 /* libsqlite3.dylib */,
08FB779EFE84155DC02AAC07 /* Foundation.framework */,
CCBE26C013B3BA8C006F6C37 /* AppKit.framework */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
Expand Down Expand Up @@ -151,6 +169,8 @@
EE42910712B42FC90088BD94 /* FMDatabase.h in Headers */,
EE42910612B42FC30088BD94 /* FMDatabaseAdditions.h in Headers */,
EE42910912B42FD00088BD94 /* FMResultSet.h in Headers */,
CC9E4EBA13B31188005F9210 /* FMDatabasePool.h in Headers */,
CC47A00F148581E9002CCDAB /* FMDatabaseQueue.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -197,8 +217,11 @@
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0430;
};
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "fmdb" */;
compatibilityVersion = "Xcode 3.1";
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
Expand Down Expand Up @@ -226,6 +249,8 @@
CCC24EC50A13E34D00A6D3E3 /* fmdb.m in Sources */,
CCC24EC70A13E34D00A6D3E3 /* FMResultSet.m in Sources */,
CC50F2CD0DF9183600E4AAAE /* FMDatabaseAdditions.m in Sources */,
CC9E4EB913B31188005F9210 /* FMDatabasePool.m in Sources */,
CC47A010148581E9002CCDAB /* FMDatabaseQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -236,6 +261,8 @@
EE42910812B42FCC0088BD94 /* FMDatabase.m in Sources */,
EE42910512B42FBC0088BD94 /* FMDatabaseAdditions.m in Sources */,
EE42910A12B42FD20088BD94 /* FMResultSet.m in Sources */,
CC9E4EBB13B31188005F9210 /* FMDatabasePool.m in Sources */,
CC47A011148581E9002CCDAB /* FMDatabaseQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -245,9 +272,11 @@
1DEB927508733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_OBJCPP_ARC_ABI = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = fmdb_Prefix.pch;
Expand All @@ -261,6 +290,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_OBJCPP_ARC_ABI = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
Expand All @@ -283,7 +314,6 @@
GCC_WARN_PEDANTIC = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
};
name = Debug;
};
Expand All @@ -294,7 +324,6 @@
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
};
name = Release;
};
Expand All @@ -305,7 +334,6 @@
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
PREBINDING = NO;
PRODUCT_NAME = FMDB;
};
name = Debug;
Expand All @@ -316,8 +344,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
PREBINDING = NO;
PRODUCT_NAME = FMDB;
ZERO_LINK = NO;
};
Expand Down
Loading

0 comments on commit b2047f9

Please sign in to comment.