mirror of
https://github.com/darlinghq/fmdb.git
synced 2024-11-23 04:29:52 +00:00
Initial import.
This commit is contained in:
parent
f8ecf5a031
commit
7ffd84d76b
24
LICENSE.txt
Normal file
24
LICENSE.txt
Normal file
@ -0,0 +1,24 @@
|
||||
If you are using fmdb in your project, I'd love to hear about it. Let me
|
||||
know at gus@flyingmeat.com.
|
||||
|
||||
In short, this is the MIT License.
|
||||
|
||||
Copyright (c) 2008 Flying Meat Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,4 @@
|
||||
FMDB
|
||||
====
|
||||
|
||||
This is an Objective-C wrapper around SQLite: http://sqlite.org/
|
79
fmdb.1
Normal file
79
fmdb.1
Normal file
@ -0,0 +1,79 @@
|
||||
.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
|
||||
.\"See Also:
|
||||
.\"man mdoc.samples for a complete listing of options
|
||||
.\"man mdoc for the short list of editing options
|
||||
.\"/usr/share/misc/mdoc.template
|
||||
.Dd 5/11/06 \" DATE
|
||||
.Dt fmdb 1 \" Program name and manual section number
|
||||
.Os Darwin
|
||||
.Sh NAME \" Section Header - required - don't modify
|
||||
.Nm fmdb,
|
||||
.\" The following lines are read in generating the apropos(man -k) database. Use only key
|
||||
.\" words here as the database is built based on the words here and in the .ND line.
|
||||
.Nm Other_name_for_same_program(),
|
||||
.Nm Yet another name for the same program.
|
||||
.\" Use .Nm macro to designate other names for the documented program.
|
||||
.Nd This line parsed for whatis database.
|
||||
.Sh SYNOPSIS \" Section Header - required - don't modify
|
||||
.Nm
|
||||
.Op Fl abcd \" [-abcd]
|
||||
.Op Fl a Ar path \" [-a path]
|
||||
.Op Ar file \" [file]
|
||||
.Op Ar \" [file ...]
|
||||
.Ar arg0 \" Underlined argument - use .Ar anywhere to underline
|
||||
arg2 ... \" Arguments
|
||||
.Sh DESCRIPTION \" Section Header - required - don't modify
|
||||
Use the .Nm macro to refer to your program throughout the man page like such:
|
||||
.Nm
|
||||
Underlining is accomplished with the .Ar macro like this:
|
||||
.Ar underlined text .
|
||||
.Pp \" Inserts a space
|
||||
A list of items with descriptions:
|
||||
.Bl -tag -width -indent \" Begins a tagged list
|
||||
.It item a \" Each item preceded by .It macro
|
||||
Description of item a
|
||||
.It item b
|
||||
Description of item b
|
||||
.El \" Ends the list
|
||||
.Pp
|
||||
A list of flags and their descriptions:
|
||||
.Bl -tag -width -indent \" Differs from above in tag removed
|
||||
.It Fl a \"-a flag as a list item
|
||||
Description of -a flag
|
||||
.It Fl b
|
||||
Description of -b flag
|
||||
.El \" Ends the list
|
||||
.Pp
|
||||
.\" .Sh ENVIRONMENT \" May not be needed
|
||||
.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
|
||||
.\" .It Ev ENV_VAR_1
|
||||
.\" Description of ENV_VAR_1
|
||||
.\" .It Ev ENV_VAR_2
|
||||
.\" Description of ENV_VAR_2
|
||||
.\" .El
|
||||
.Sh FILES \" File used or created by the topic of the man page
|
||||
.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
|
||||
.It Pa /usr/share/file_name
|
||||
FILE_1 description
|
||||
.It Pa /Users/joeuser/Library/really_long_file_name
|
||||
FILE_2 description
|
||||
.El \" Ends the list
|
||||
.\" .Sh DIAGNOSTICS \" May not be needed
|
||||
.\" .Bl -diag
|
||||
.\" .It Diagnostic Tag
|
||||
.\" Diagnostic informtion here.
|
||||
.\" .It Diagnostic Tag
|
||||
.\" Diagnostic informtion here.
|
||||
.\" .El
|
||||
.Sh SEE ALSO
|
||||
.\" List links in ascending order by section, alphabetically within a section.
|
||||
.\" Please do not reference files that do not exist without filing a bug report
|
||||
.Xr a 1 ,
|
||||
.Xr b 1 ,
|
||||
.Xr c 1 ,
|
||||
.Xr a 2 ,
|
||||
.Xr b 2 ,
|
||||
.Xr a 3 ,
|
||||
.Xr b 3
|
||||
.\" .Sh BUGS \" Document known, unremedied bugs
|
||||
.\" .Sh HISTORY \" Document history if command behaves in a unique manner
|
259
fmdb.xcodeproj/project.pbxproj
Normal file
259
fmdb.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,259 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 45;
|
||||
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 */; };
|
||||
CC50F2CD0DF9183600E4AAAE /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CC50F2CB0DF9183600E4AAAE /* FMDatabaseAdditions.m */; };
|
||||
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 */; };
|
||||
CCC24EC50A13E34D00A6D3E3 /* fmdb.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC24EBE0A13E34D00A6D3E3 /* fmdb.m */; };
|
||||
CCC24EC60A13E34D00A6D3E3 /* FMResultSet.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CCC24EBF0A13E34D00A6D3E3 /* FMResultSet.h */; };
|
||||
CCC24EC70A13E34D00A6D3E3 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC24EC00A13E34D00A6D3E3 /* FMResultSet.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 8;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
8DD76F9F0486AA7600D96B5E /* fmdb.1 in CopyFiles */,
|
||||
CCC24EC10A13E34D00A6D3E3 /* FMDatabase.h in CopyFiles */,
|
||||
CCC24EC60A13E34D00A6D3E3 /* FMResultSet.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
CCC24EBE0A13E34D00A6D3E3 /* fmdb.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = fmdb.m; path = src/fmdb.m; sourceTree = "<group>"; };
|
||||
CCC24EBF0A13E34D00A6D3E3 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMResultSet.h; path = src/FMResultSet.h; sourceTree = "<group>"; };
|
||||
CCC24EC00A13E34D00A6D3E3 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMResultSet.m; path = src/FMResultSet.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
|
||||
CCBEBDAC0DF5DE1A003DDD08 /* libsqlite3.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
08FB7794FE84155DC02AAC07 /* fmdb */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC8C138B0E3135C400FBE1E7 /* LICENSE.txt */,
|
||||
CC8C138A0E3135C400FBE1E7 /* CHANGES_AND_TODO_LIST.txt */,
|
||||
CC8C138C0E3135C400FBE1E7 /* CONTRIBUTORS.txt */,
|
||||
08FB7795FE84155DC02AAC07 /* Source */,
|
||||
C6859EA2029092E104C91782 /* Documentation */,
|
||||
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
);
|
||||
name = fmdb;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB7795FE84155DC02AAC07 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CC50F2CB0DF9183600E4AAAE /* FMDatabaseAdditions.m */,
|
||||
CC50F2CC0DF9183600E4AAAE /* FMDatabaseAdditions.h */,
|
||||
CCC24EBA0A13E34D00A6D3E3 /* FMDatabase.h */,
|
||||
CCC24EBB0A13E34D00A6D3E3 /* FMDatabase.m */,
|
||||
CCC24EBF0A13E34D00A6D3E3 /* FMResultSet.h */,
|
||||
CCC24EC00A13E34D00A6D3E3 /* FMResultSet.m */,
|
||||
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */,
|
||||
CCC24EBE0A13E34D00A6D3E3 /* fmdb.m */,
|
||||
);
|
||||
name = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CCBEBDAB0DF5DE1A003DDD08 /* libsqlite3.dylib */,
|
||||
08FB779EFE84155DC02AAC07 /* Foundation.framework */,
|
||||
);
|
||||
name = "External Frameworks and Libraries";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8DD76FA10486AA7600D96B5E /* fmdb */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6859EA2029092E104C91782 /* Documentation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6859EA3029092ED04C91782 /* fmdb.1 */,
|
||||
);
|
||||
name = Documentation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8DD76F960486AA7600D96B5E /* fmdb */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb" */;
|
||||
buildPhases = (
|
||||
8DD76F990486AA7600D96B5E /* Sources */,
|
||||
8DD76F9B0486AA7600D96B5E /* Frameworks */,
|
||||
8DD76F9E0486AA7600D96B5E /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = fmdb;
|
||||
productInstallPath = "$(HOME)/bin";
|
||||
productName = fmdb;
|
||||
productReference = 8DD76FA10486AA7600D96B5E /* fmdb */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "fmdb" */;
|
||||
compatibilityVersion = "Xcode 3.1";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 08FB7794FE84155DC02AAC07 /* fmdb */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8DD76F960486AA7600D96B5E /* fmdb */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8DD76F990486AA7600D96B5E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CCC24EC20A13E34D00A6D3E3 /* FMDatabase.m in Sources */,
|
||||
CCC24EC50A13E34D00A6D3E3 /* fmdb.m in Sources */,
|
||||
CCC24EC70A13E34D00A6D3E3 /* FMResultSet.m in Sources */,
|
||||
CC50F2CD0DF9183600E4AAAE /* FMDatabaseAdditions.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB927508733DD40010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = fmdb_Prefix.pch;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(LIBRARY_SEARCH_PATHS)",
|
||||
"$(SRCROOT)/sqlite",
|
||||
);
|
||||
PRODUCT_NAME = fmdb;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB927608733DD40010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = fmdb_Prefix.pch;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(LIBRARY_SEARCH_PATHS)",
|
||||
"$(SRCROOT)/sqlite",
|
||||
);
|
||||
PRODUCT_NAME = fmdb;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB927908733DD40010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = i386;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB927A08733DD40010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB927508733DD40010E9CD /* Debug */,
|
||||
1DEB927608733DD40010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "fmdb" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB927908733DD40010E9CD /* Debug */,
|
||||
1DEB927A08733DD40010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||
}
|
7
fmdb_Prefix.pch
Normal file
7
fmdb_Prefix.pch
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'fmdb' target in the 'fmdb' project.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
116
src/FMDatabase.h
Normal file
116
src/FMDatabase.h
Normal file
@ -0,0 +1,116 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "sqlite3.h"
|
||||
#import "FMResultSet.h"
|
||||
|
||||
@interface FMDatabase : NSObject
|
||||
{
|
||||
sqlite3* db;
|
||||
NSString* databasePath;
|
||||
BOOL logsErrors;
|
||||
BOOL crashOnErrors;
|
||||
BOOL inUse;
|
||||
BOOL inTransaction;
|
||||
BOOL traceExecution;
|
||||
BOOL checkedOut;
|
||||
int busyRetryTimeout;
|
||||
BOOL shouldCacheStatements;
|
||||
NSMutableDictionary *cachedStatements;
|
||||
}
|
||||
|
||||
|
||||
+ (id)databaseWithPath:(NSString*)inPath;
|
||||
- (id)initWithPath:(NSString*)inPath;
|
||||
|
||||
- (BOOL) open;
|
||||
#if SQLITE_VERSION_NUMBER >= 3005000
|
||||
- (BOOL) openWithFlags:(int)flags;
|
||||
#endif
|
||||
- (BOOL) close;
|
||||
- (BOOL) goodConnection;
|
||||
- (void) clearCachedStatements;
|
||||
|
||||
// encryption methods. You need to have purchased the sqlite encryption extensions for these to work.
|
||||
- (BOOL) setKey:(NSString*)key;
|
||||
- (BOOL) rekey:(NSString*)key;
|
||||
|
||||
|
||||
- (NSString *) databasePath;
|
||||
|
||||
- (NSString*) lastErrorMessage;
|
||||
|
||||
- (int) lastErrorCode;
|
||||
- (BOOL) hadError;
|
||||
- (sqlite_int64) lastInsertRowId;
|
||||
|
||||
- (sqlite3*) sqliteHandle;
|
||||
|
||||
- (BOOL) executeUpdate:(NSString*)sql, ...;
|
||||
- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
|
||||
- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead.
|
||||
|
||||
- (id) executeQuery:(NSString*)sql, ...;
|
||||
- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
|
||||
- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead.
|
||||
|
||||
- (BOOL) rollback;
|
||||
- (BOOL) commit;
|
||||
- (BOOL) beginTransaction;
|
||||
- (BOOL) beginDeferredTransaction;
|
||||
|
||||
- (BOOL)logsErrors;
|
||||
- (void)setLogsErrors:(BOOL)flag;
|
||||
|
||||
- (BOOL)crashOnErrors;
|
||||
- (void)setCrashOnErrors:(BOOL)flag;
|
||||
|
||||
- (BOOL)inUse;
|
||||
- (void)setInUse:(BOOL)value;
|
||||
|
||||
- (BOOL)inTransaction;
|
||||
- (void)setInTransaction:(BOOL)flag;
|
||||
|
||||
- (BOOL)traceExecution;
|
||||
- (void)setTraceExecution:(BOOL)flag;
|
||||
|
||||
- (BOOL)checkedOut;
|
||||
- (void)setCheckedOut:(BOOL)flag;
|
||||
|
||||
- (int)busyRetryTimeout;
|
||||
- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout;
|
||||
|
||||
- (BOOL)shouldCacheStatements;
|
||||
- (void)setShouldCacheStatements:(BOOL)value;
|
||||
|
||||
- (NSMutableDictionary *)cachedStatements;
|
||||
- (void)setCachedStatements:(NSMutableDictionary *)value;
|
||||
|
||||
|
||||
+ (NSString*) sqliteLibVersion;
|
||||
|
||||
|
||||
- (int)changes;
|
||||
|
||||
@end
|
||||
|
||||
@interface FMStatement : NSObject {
|
||||
sqlite3_stmt *statement;
|
||||
NSString *query;
|
||||
long useCount;
|
||||
}
|
||||
|
||||
|
||||
- (void) close;
|
||||
- (void) reset;
|
||||
|
||||
- (sqlite3_stmt *)statement;
|
||||
- (void)setStatement:(sqlite3_stmt *)value;
|
||||
|
||||
- (NSString *)query;
|
||||
- (void)setQuery:(NSString *)value;
|
||||
|
||||
- (long)useCount;
|
||||
- (void)setUseCount:(long)value;
|
||||
|
||||
|
||||
@end
|
||||
|
753
src/FMDatabase.m
Normal file
753
src/FMDatabase.m
Normal file
@ -0,0 +1,753 @@
|
||||
#import "FMDatabase.h"
|
||||
#import "unistd.h"
|
||||
|
||||
@implementation FMDatabase
|
||||
|
||||
+ (id)databaseWithPath:(NSString*)aPath {
|
||||
return [[[self alloc] initWithPath:aPath] autorelease];
|
||||
}
|
||||
|
||||
- (id)initWithPath:(NSString*)aPath {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
databasePath = [aPath copy];
|
||||
db = 0x00;
|
||||
logsErrors = 0x00;
|
||||
crashOnErrors = 0x00;
|
||||
busyRetryTimeout = 0x00;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
|
||||
[cachedStatements release];
|
||||
[databasePath release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (NSString*) sqliteLibVersion {
|
||||
return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
|
||||
}
|
||||
|
||||
- (NSString *) databasePath {
|
||||
return databasePath;
|
||||
}
|
||||
|
||||
- (sqlite3*) sqliteHandle {
|
||||
return db;
|
||||
}
|
||||
|
||||
- (BOOL) open {
|
||||
int err = sqlite3_open([databasePath fileSystemRepresentation], &db );
|
||||
if(err != SQLITE_OK) {
|
||||
NSLog(@"error opening!: %d", err);
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#if SQLITE_VERSION_NUMBER >= 3005000
|
||||
- (BOOL) openWithFlags:(int)flags {
|
||||
int err = sqlite3_open_v2([databasePath fileSystemRepresentation], &db, flags, NULL /* Name of VFS module to use */);
|
||||
if(err != SQLITE_OK) {
|
||||
NSLog(@"error opening!: %d", err);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
- (BOOL) close {
|
||||
|
||||
[self clearCachedStatements];
|
||||
|
||||
if (!db) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
int rc;
|
||||
BOOL retry;
|
||||
int numberOfRetries = 0;
|
||||
do {
|
||||
retry = NO;
|
||||
rc = sqlite3_close(db);
|
||||
if (SQLITE_BUSY == rc) {
|
||||
retry = YES;
|
||||
usleep(20);
|
||||
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
|
||||
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
|
||||
NSLog(@"Database busy, unable to close");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (SQLITE_OK != rc) {
|
||||
NSLog(@"error closing!: %d", rc);
|
||||
}
|
||||
}
|
||||
while (retry);
|
||||
|
||||
db = nil;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) clearCachedStatements {
|
||||
|
||||
NSEnumerator *e = [cachedStatements objectEnumerator];
|
||||
FMStatement *cachedStmt;
|
||||
|
||||
while ((cachedStmt = [e nextObject])) {
|
||||
[cachedStmt close];
|
||||
}
|
||||
|
||||
[cachedStatements removeAllObjects];
|
||||
}
|
||||
|
||||
- (FMStatement*) cachedStatementForQuery:(NSString*)query {
|
||||
return [cachedStatements objectForKey:query];
|
||||
}
|
||||
|
||||
- (void) setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
|
||||
//NSLog(@"setting query: %@", query);
|
||||
query = [query copy]; // in case we got handed in a mutable string...
|
||||
[statement setQuery:query];
|
||||
[cachedStatements setObject:statement forKey:query];
|
||||
[query release];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) rekey:(NSString*)key {
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if (!key) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String]));
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
NSLog(@"error on rekey: %d", rc);
|
||||
NSLog(@"%@", [self lastErrorMessage]);
|
||||
}
|
||||
|
||||
return (rc == SQLITE_OK);
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL) setKey:(NSString*)key {
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if (!key) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String]));
|
||||
|
||||
return (rc == SQLITE_OK);
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL) goodConnection {
|
||||
|
||||
if (!db) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
|
||||
|
||||
if (rs) {
|
||||
[rs close];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) compainAboutInUse {
|
||||
NSLog(@"The FMDatabase %@ is currently in use.", self);
|
||||
|
||||
if (crashOnErrors) {
|
||||
NSAssert1(false, @"The FMDatabase %@ is currently in use.", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*) lastErrorMessage {
|
||||
return [NSString stringWithUTF8String:sqlite3_errmsg(db)];
|
||||
}
|
||||
|
||||
- (BOOL) hadError {
|
||||
int lastErrCode = [self lastErrorCode];
|
||||
|
||||
return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
|
||||
}
|
||||
|
||||
- (int) lastErrorCode {
|
||||
return sqlite3_errcode(db);
|
||||
}
|
||||
|
||||
- (sqlite_int64) lastInsertRowId {
|
||||
|
||||
if (inUse) {
|
||||
[self compainAboutInUse];
|
||||
return NO;
|
||||
}
|
||||
[self setInUse:YES];
|
||||
|
||||
sqlite_int64 ret = sqlite3_last_insert_rowid(db);
|
||||
|
||||
[self setInUse:NO];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void) bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; {
|
||||
|
||||
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
|
||||
sqlite3_bind_null(pStmt, idx);
|
||||
}
|
||||
|
||||
// FIXME - someday check the return codes on these binds.
|
||||
else if ([obj isKindOfClass:[NSData class]]) {
|
||||
sqlite3_bind_blob(pStmt, idx, [obj bytes], (int)[obj length], SQLITE_STATIC);
|
||||
}
|
||||
else if ([obj isKindOfClass:[NSDate class]]) {
|
||||
sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
|
||||
}
|
||||
else if ([obj isKindOfClass:[NSNumber class]]) {
|
||||
|
||||
if (strcmp([obj objCType], @encode(BOOL)) == 0) {
|
||||
sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
|
||||
}
|
||||
else if (strcmp([obj objCType], @encode(int)) == 0) {
|
||||
sqlite3_bind_int64(pStmt, idx, [obj longValue]);
|
||||
}
|
||||
else if (strcmp([obj objCType], @encode(long)) == 0) {
|
||||
sqlite3_bind_int64(pStmt, idx, [obj longValue]);
|
||||
}
|
||||
else if (strcmp([obj objCType], @encode(long long)) == 0) {
|
||||
sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
|
||||
}
|
||||
else if (strcmp([obj objCType], @encode(float)) == 0) {
|
||||
sqlite3_bind_double(pStmt, idx, [obj floatValue]);
|
||||
}
|
||||
else if (strcmp([obj objCType], @encode(double)) == 0) {
|
||||
sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
|
||||
}
|
||||
else {
|
||||
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
|
||||
- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
|
||||
|
||||
if (inUse) {
|
||||
[self compainAboutInUse];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self setInUse:YES];
|
||||
|
||||
FMResultSet *rs = nil;
|
||||
|
||||
int rc = 0x00;;
|
||||
sqlite3_stmt *pStmt = 0x00;;
|
||||
FMStatement *statement = 0x00;
|
||||
|
||||
if (traceExecution && sql) {
|
||||
NSLog(@"%@ executeQuery: %@", self, sql);
|
||||
}
|
||||
|
||||
if (shouldCacheStatements) {
|
||||
statement = [self cachedStatementForQuery:sql];
|
||||
pStmt = statement ? [statement statement] : 0x00;
|
||||
}
|
||||
|
||||
int numberOfRetries = 0;
|
||||
BOOL retry = NO;
|
||||
|
||||
if (!pStmt) {
|
||||
do {
|
||||
retry = NO;
|
||||
rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0);
|
||||
|
||||
if (SQLITE_BUSY == rc) {
|
||||
retry = YES;
|
||||
usleep(20);
|
||||
|
||||
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
|
||||
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
|
||||
NSLog(@"Database busy");
|
||||
sqlite3_finalize(pStmt);
|
||||
[self setInUse:NO];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
else if (SQLITE_OK != rc) {
|
||||
|
||||
|
||||
if (logsErrors) {
|
||||
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
|
||||
NSLog(@"DB Query: %@", sql);
|
||||
if (crashOnErrors) {
|
||||
//#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR
|
||||
// asm{ trap };
|
||||
//#endif
|
||||
NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
[self setInUse:NO];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
while (retry);
|
||||
}
|
||||
|
||||
id obj;
|
||||
int idx = 0;
|
||||
int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
|
||||
|
||||
while (idx < queryCount) {
|
||||
|
||||
if (arrayArgs) {
|
||||
obj = [arrayArgs objectAtIndex:idx];
|
||||
}
|
||||
else {
|
||||
obj = va_arg(args, id);
|
||||
}
|
||||
|
||||
if (traceExecution) {
|
||||
NSLog(@"obj: %@", obj);
|
||||
}
|
||||
|
||||
idx++;
|
||||
|
||||
[self bindObject:obj toColumn:idx inStatement:pStmt];
|
||||
}
|
||||
|
||||
if (idx != queryCount) {
|
||||
NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
|
||||
sqlite3_finalize(pStmt);
|
||||
[self setInUse:NO];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[statement retain]; // to balance the release below
|
||||
|
||||
if (!statement) {
|
||||
statement = [[FMStatement alloc] init];
|
||||
[statement setStatement:pStmt];
|
||||
|
||||
if (shouldCacheStatements) {
|
||||
[self setCachedStatement:statement forQuery:sql];
|
||||
}
|
||||
}
|
||||
|
||||
// the statement gets close in rs's dealloc or [rs close];
|
||||
rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
|
||||
[rs setQuery:sql];
|
||||
|
||||
statement.useCount = statement.useCount + 1;
|
||||
|
||||
[statement release];
|
||||
|
||||
[self setInUse:NO];
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
- (id) executeQuery:(NSString*)sql, ... {
|
||||
va_list args;
|
||||
va_start(args, sql);
|
||||
|
||||
id result = [self executeQuery:sql withArgumentsInArray:nil orVAList:args];
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id) executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
|
||||
return [self executeQuery:sql withArgumentsInArray:arguments orVAList:nil];
|
||||
}
|
||||
|
||||
- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
|
||||
|
||||
if (inUse) {
|
||||
[self compainAboutInUse];
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self setInUse:YES];
|
||||
|
||||
int rc = 0x00;
|
||||
sqlite3_stmt *pStmt = 0x00;
|
||||
FMStatement *cachedStmt = 0x00;
|
||||
|
||||
if (traceExecution && sql) {
|
||||
NSLog(@"%@ executeUpdate: %@", self, sql);
|
||||
}
|
||||
|
||||
if (shouldCacheStatements) {
|
||||
cachedStmt = [self cachedStatementForQuery:sql];
|
||||
pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
|
||||
}
|
||||
|
||||
int numberOfRetries = 0;
|
||||
BOOL retry = NO;
|
||||
|
||||
if (!pStmt) {
|
||||
|
||||
do {
|
||||
retry = NO;
|
||||
rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0);
|
||||
if (SQLITE_BUSY == rc) {
|
||||
retry = YES;
|
||||
usleep(20);
|
||||
|
||||
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
|
||||
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
|
||||
NSLog(@"Database busy");
|
||||
sqlite3_finalize(pStmt);
|
||||
[self setInUse:NO];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (SQLITE_OK != rc) {
|
||||
|
||||
|
||||
if (logsErrors) {
|
||||
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
|
||||
NSLog(@"DB Query: %@", sql);
|
||||
if (crashOnErrors) {
|
||||
//#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR
|
||||
// asm{ trap };
|
||||
//#endif
|
||||
NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(pStmt);
|
||||
[self setInUse:NO];
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
while (retry);
|
||||
}
|
||||
|
||||
|
||||
id obj;
|
||||
int idx = 0;
|
||||
int queryCount = sqlite3_bind_parameter_count(pStmt);
|
||||
|
||||
while (idx < queryCount) {
|
||||
|
||||
if (arrayArgs) {
|
||||
obj = [arrayArgs objectAtIndex:idx];
|
||||
}
|
||||
else {
|
||||
obj = va_arg(args, id);
|
||||
}
|
||||
|
||||
|
||||
if (traceExecution) {
|
||||
NSLog(@"obj: %@", obj);
|
||||
}
|
||||
|
||||
idx++;
|
||||
|
||||
[self bindObject:obj toColumn:idx inStatement:pStmt];
|
||||
}
|
||||
|
||||
if (idx != queryCount) {
|
||||
NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
|
||||
sqlite3_finalize(pStmt);
|
||||
[self setInUse:NO];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* Call sqlite3_step() to run the virtual machine. Since the SQL being
|
||||
** executed is not a SELECT statement, we assume no data will be returned.
|
||||
*/
|
||||
numberOfRetries = 0;
|
||||
do {
|
||||
rc = sqlite3_step(pStmt);
|
||||
retry = NO;
|
||||
|
||||
if (SQLITE_BUSY == rc) {
|
||||
// this will happen if the db is locked, like if we are doing an update or insert.
|
||||
// in that case, retry the step... and maybe wait just 10 milliseconds.
|
||||
retry = YES;
|
||||
usleep(20);
|
||||
|
||||
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
|
||||
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
|
||||
NSLog(@"Database busy");
|
||||
retry = NO;
|
||||
}
|
||||
}
|
||||
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
|
||||
// all is well, let's return.
|
||||
}
|
||||
else if (SQLITE_ERROR == rc) {
|
||||
NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(db));
|
||||
NSLog(@"DB Query: %@", sql);
|
||||
}
|
||||
else if (SQLITE_MISUSE == rc) {
|
||||
// uh oh.
|
||||
NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(db));
|
||||
NSLog(@"DB Query: %@", sql);
|
||||
}
|
||||
else {
|
||||
// wtf?
|
||||
NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
|
||||
NSLog(@"DB Query: %@", sql);
|
||||
}
|
||||
|
||||
} while (retry);
|
||||
|
||||
assert( rc!=SQLITE_ROW );
|
||||
|
||||
|
||||
if (shouldCacheStatements && !cachedStmt) {
|
||||
cachedStmt = [[FMStatement alloc] init];
|
||||
|
||||
[cachedStmt setStatement:pStmt];
|
||||
|
||||
[self setCachedStatement:cachedStmt forQuery:sql];
|
||||
|
||||
[cachedStmt release];
|
||||
}
|
||||
|
||||
if (cachedStmt) {
|
||||
cachedStmt.useCount = cachedStmt.useCount + 1;
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
else {
|
||||
/* Finalize the virtual machine. This releases all memory and other
|
||||
** resources allocated by the sqlite3_prepare() call above.
|
||||
*/
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
[self setInUse:NO];
|
||||
|
||||
return (rc == SQLITE_OK);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) executeUpdate:(NSString*)sql, ... {
|
||||
va_list args;
|
||||
va_start(args, sql);
|
||||
|
||||
BOOL result = [self executeUpdate:sql withArgumentsInArray:nil orVAList:args];
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL) executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
|
||||
return [self executeUpdate:sql withArgumentsInArray:arguments orVAList:nil];
|
||||
}
|
||||
|
||||
/*
|
||||
- (id) executeUpdate:(NSString *)sql arguments:(va_list)args {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
- (BOOL) rollback {
|
||||
BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"];
|
||||
if (b) {
|
||||
inTransaction = NO;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
- (BOOL) commit {
|
||||
BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"];
|
||||
if (b) {
|
||||
inTransaction = NO;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
- (BOOL) beginDeferredTransaction {
|
||||
BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"];
|
||||
if (b) {
|
||||
inTransaction = YES;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
- (BOOL) beginTransaction {
|
||||
BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"];
|
||||
if (b) {
|
||||
inTransaction = YES;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
- (BOOL)logsErrors {
|
||||
return logsErrors;
|
||||
}
|
||||
- (void)setLogsErrors:(BOOL)flag {
|
||||
logsErrors = flag;
|
||||
}
|
||||
|
||||
- (BOOL)crashOnErrors {
|
||||
return crashOnErrors;
|
||||
}
|
||||
- (void)setCrashOnErrors:(BOOL)flag {
|
||||
crashOnErrors = flag;
|
||||
}
|
||||
|
||||
- (BOOL)inUse {
|
||||
return inUse || inTransaction;
|
||||
}
|
||||
|
||||
- (void) setInUse:(BOOL)b {
|
||||
inUse = b;
|
||||
}
|
||||
|
||||
- (BOOL)inTransaction {
|
||||
return inTransaction;
|
||||
}
|
||||
- (void)setInTransaction:(BOOL)flag {
|
||||
inTransaction = flag;
|
||||
}
|
||||
|
||||
- (BOOL)traceExecution {
|
||||
return traceExecution;
|
||||
}
|
||||
- (void)setTraceExecution:(BOOL)flag {
|
||||
traceExecution = flag;
|
||||
}
|
||||
|
||||
- (BOOL)checkedOut {
|
||||
return checkedOut;
|
||||
}
|
||||
- (void)setCheckedOut:(BOOL)flag {
|
||||
checkedOut = flag;
|
||||
}
|
||||
|
||||
|
||||
- (int)busyRetryTimeout {
|
||||
return busyRetryTimeout;
|
||||
}
|
||||
- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout {
|
||||
busyRetryTimeout = newBusyRetryTimeout;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)shouldCacheStatements {
|
||||
return shouldCacheStatements;
|
||||
}
|
||||
|
||||
- (void)setShouldCacheStatements:(BOOL)value {
|
||||
|
||||
shouldCacheStatements = value;
|
||||
|
||||
if (shouldCacheStatements && !cachedStatements) {
|
||||
[self setCachedStatements:[NSMutableDictionary dictionary]];
|
||||
}
|
||||
|
||||
if (!shouldCacheStatements) {
|
||||
[self setCachedStatements:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) cachedStatements {
|
||||
return cachedStatements;
|
||||
}
|
||||
|
||||
- (void)setCachedStatements:(NSMutableDictionary *)value {
|
||||
if (cachedStatements != value) {
|
||||
[cachedStatements release];
|
||||
cachedStatements = [value retain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (int)changes {
|
||||
return(sqlite3_changes(db));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation FMStatement
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
[query release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
- (void) close {
|
||||
if (statement) {
|
||||
sqlite3_finalize(statement);
|
||||
statement = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) reset {
|
||||
if (statement) {
|
||||
sqlite3_reset(statement);
|
||||
}
|
||||
}
|
||||
|
||||
- (sqlite3_stmt *) statement {
|
||||
return statement;
|
||||
}
|
||||
|
||||
- (void)setStatement:(sqlite3_stmt *)value {
|
||||
statement = value;
|
||||
}
|
||||
|
||||
- (NSString *) query {
|
||||
return query;
|
||||
}
|
||||
|
||||
- (void)setQuery:(NSString *)value {
|
||||
if (query != value) {
|
||||
[query release];
|
||||
query = [value retain];
|
||||
}
|
||||
}
|
||||
|
||||
- (long)useCount {
|
||||
return useCount;
|
||||
}
|
||||
|
||||
- (void)setUseCount:(long)value {
|
||||
if (useCount != value) {
|
||||
useCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*) description {
|
||||
return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], useCount, query];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
31
src/FMDatabaseAdditions.h
Normal file
31
src/FMDatabaseAdditions.h
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// FMDatabaseAdditions.h
|
||||
// fmkit
|
||||
//
|
||||
// Created by August Mueller on 10/30/05.
|
||||
// Copyright 2005 Flying Meat Inc.. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface FMDatabase (FMDatabaseAdditions)
|
||||
|
||||
|
||||
- (int)intForQuery:(NSString*)objs, ...;
|
||||
- (long)longForQuery:(NSString*)objs, ...;
|
||||
- (BOOL)boolForQuery:(NSString*)objs, ...;
|
||||
- (double)doubleForQuery:(NSString*)objs, ...;
|
||||
- (NSString*)stringForQuery:(NSString*)objs, ...;
|
||||
- (NSData*)dataForQuery:(NSString*)objs, ...;
|
||||
- (NSDate*)dateForQuery:(NSString*)objs, ...;
|
||||
|
||||
// Notice that there's no dataNoCopyForQuery:.
|
||||
// That would be a bad idea, because we close out the result set, and then what
|
||||
// happens to the data that we just didn't copy? Who knows, not I.
|
||||
|
||||
|
||||
- (BOOL)tableExists:(NSString*)tableName;
|
||||
- (FMResultSet*)getSchema;
|
||||
- (FMResultSet*)getTableSchema:(NSString*)tableName;
|
||||
- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName;
|
||||
|
||||
@end
|
114
src/FMDatabaseAdditions.m
Normal file
114
src/FMDatabaseAdditions.m
Normal file
@ -0,0 +1,114 @@
|
||||
//
|
||||
// FMDatabaseAdditions.m
|
||||
// fmkit
|
||||
//
|
||||
// Created by August Mueller on 10/30/05.
|
||||
// Copyright 2005 Flying Meat Inc.. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FMDatabase.h"
|
||||
#import "FMDatabaseAdditions.h"
|
||||
|
||||
@implementation FMDatabase (FMDatabaseAdditions)
|
||||
|
||||
#define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \
|
||||
va_list args; \
|
||||
va_start(args, query); \
|
||||
FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orVAList:args]; \
|
||||
va_end(args); \
|
||||
if (![resultSet next]) { return (type)0; } \
|
||||
type ret = [resultSet sel:0]; \
|
||||
[resultSet close]; \
|
||||
[resultSet setParentDB:nil]; \
|
||||
return ret;
|
||||
|
||||
|
||||
- (NSString*)stringForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
|
||||
}
|
||||
|
||||
- (int)intForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex);
|
||||
}
|
||||
|
||||
- (long)longForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex);
|
||||
}
|
||||
|
||||
- (BOOL)boolForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex);
|
||||
}
|
||||
|
||||
- (double)doubleForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex);
|
||||
}
|
||||
|
||||
- (NSData*)dataForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex);
|
||||
}
|
||||
|
||||
- (NSDate*)dateForQuery:(NSString*)query, ...; {
|
||||
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex);
|
||||
}
|
||||
|
||||
|
||||
//check if table exist in database (patch from OZLB)
|
||||
- (BOOL)tableExists:(NSString*)tableName {
|
||||
|
||||
BOOL returnBool;
|
||||
//lower case table name
|
||||
tableName = [tableName lowercaseString];
|
||||
//search in sqlite_master table if table exists
|
||||
FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName];
|
||||
//if at least one next exists, table exists
|
||||
returnBool = [rs next];
|
||||
//close and free object
|
||||
[rs close];
|
||||
|
||||
return returnBool;
|
||||
}
|
||||
|
||||
//get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
|
||||
//check if table exist in database (patch from OZLB)
|
||||
- (FMResultSet*)getSchema {
|
||||
|
||||
//result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
|
||||
FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"];
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
//get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
|
||||
- (FMResultSet*)getTableSchema:(NSString*)tableName {
|
||||
|
||||
//result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
|
||||
FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"PRAGMA table_info(%@)", tableName]];
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
//check if column exist in table
|
||||
- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName {
|
||||
|
||||
BOOL returnBool = NO;
|
||||
//lower case table name
|
||||
tableName = [tableName lowercaseString];
|
||||
//lower case column name
|
||||
columnName = [columnName lowercaseString];
|
||||
//get table schema
|
||||
FMResultSet *rs = [self getTableSchema: tableName];
|
||||
//check if column is present in table schema
|
||||
while ([rs next]) {
|
||||
if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString: columnName]) {
|
||||
returnBool = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//close and free object
|
||||
[rs close];
|
||||
|
||||
return returnBool;
|
||||
}
|
||||
|
||||
@end
|
75
src/FMResultSet.h
Normal file
75
src/FMResultSet.h
Normal file
@ -0,0 +1,75 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "sqlite3.h"
|
||||
|
||||
@class FMDatabase;
|
||||
@class FMStatement;
|
||||
|
||||
@interface FMResultSet : NSObject {
|
||||
FMDatabase *parentDB;
|
||||
FMStatement *statement;
|
||||
|
||||
NSString *query;
|
||||
NSMutableDictionary *columnNameToIndexMap;
|
||||
BOOL columnNamesSetup;
|
||||
}
|
||||
|
||||
|
||||
+ (id) resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB;
|
||||
|
||||
- (void) close;
|
||||
|
||||
- (NSString *)query;
|
||||
- (void)setQuery:(NSString *)value;
|
||||
|
||||
- (FMStatement *)statement;
|
||||
- (void)setStatement:(FMStatement *)value;
|
||||
|
||||
- (void)setParentDB:(FMDatabase *)newDb;
|
||||
|
||||
- (BOOL) next;
|
||||
- (BOOL) hasAnotherRow;
|
||||
|
||||
- (int) columnIndexForName:(NSString*)columnName;
|
||||
- (NSString*) columnNameForIndex:(int)columnIdx;
|
||||
|
||||
- (int) intForColumn:(NSString*)columnName;
|
||||
- (int) intForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (long) longForColumn:(NSString*)columnName;
|
||||
- (long) longForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (long long int) longLongIntForColumn:(NSString*)columnName;
|
||||
- (long long int) longLongIntForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (BOOL) boolForColumn:(NSString*)columnName;
|
||||
- (BOOL) boolForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (double) doubleForColumn:(NSString*)columnName;
|
||||
- (double) doubleForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (NSString*) stringForColumn:(NSString*)columnName;
|
||||
- (NSString*) stringForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (NSDate*) dateForColumn:(NSString*)columnName;
|
||||
- (NSDate*) dateForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (NSData*) dataForColumn:(NSString*)columnName;
|
||||
- (NSData*) dataForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (const unsigned char *) UTF8StringForColumnIndex:(int)columnIdx;
|
||||
- (const unsigned char *) UTF8StringForColumnName:(NSString*)columnName;
|
||||
|
||||
/*
|
||||
If you are going to use this data after you iterate over the next row, or after you close the
|
||||
result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:)
|
||||
If you don't, you're going to be in a world of hurt when you try and use the data.
|
||||
*/
|
||||
- (NSData*) dataNoCopyForColumn:(NSString*)columnName;
|
||||
- (NSData*) dataNoCopyForColumnIndex:(int)columnIdx;
|
||||
|
||||
- (BOOL) columnIndexIsNull:(int)columnIdx;
|
||||
- (BOOL) columnIsNull:(NSString*)columnName;
|
||||
|
||||
- (void) kvcMagic:(id)object;
|
||||
|
||||
@end
|
332
src/FMResultSet.m
Normal file
332
src/FMResultSet.m
Normal file
@ -0,0 +1,332 @@
|
||||
#import "FMResultSet.h"
|
||||
#import "FMDatabase.h"
|
||||
#import "unistd.h"
|
||||
|
||||
@interface FMResultSet (Private)
|
||||
- (NSMutableDictionary *)columnNameToIndexMap;
|
||||
- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value;
|
||||
@end
|
||||
|
||||
@implementation FMResultSet
|
||||
|
||||
+ (id) resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
|
||||
|
||||
FMResultSet *rs = [[FMResultSet alloc] init];
|
||||
|
||||
[rs setStatement:statement];
|
||||
[rs setParentDB:aDB];
|
||||
|
||||
return [rs autorelease];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self close];
|
||||
|
||||
[query release];
|
||||
query = nil;
|
||||
|
||||
[columnNameToIndexMap release];
|
||||
columnNameToIndexMap = nil;
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) close {
|
||||
|
||||
[statement reset];
|
||||
[statement release];
|
||||
statement = nil;
|
||||
|
||||
// we don't need this anymore... (i think)
|
||||
//[parentDB setInUse:NO];
|
||||
parentDB = nil;
|
||||
}
|
||||
|
||||
- (void) setupColumnNames {
|
||||
|
||||
if (!columnNameToIndexMap) {
|
||||
[self setColumnNameToIndexMap:[NSMutableDictionary dictionary]];
|
||||
}
|
||||
|
||||
int columnCount = sqlite3_column_count(statement.statement);
|
||||
|
||||
int columnIdx = 0;
|
||||
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
|
||||
[columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx]
|
||||
forKey:[[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)] lowercaseString]];
|
||||
}
|
||||
columnNamesSetup = YES;
|
||||
}
|
||||
|
||||
- (void) kvcMagic:(id)object {
|
||||
|
||||
int columnCount = sqlite3_column_count(statement.statement);
|
||||
|
||||
int columnIdx = 0;
|
||||
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
|
||||
|
||||
const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx);
|
||||
|
||||
// check for a null row
|
||||
if (c) {
|
||||
NSString *s = [NSString stringWithUTF8String:c];
|
||||
|
||||
[object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) next {
|
||||
|
||||
int rc;
|
||||
BOOL retry;
|
||||
int numberOfRetries = 0;
|
||||
do {
|
||||
retry = NO;
|
||||
|
||||
rc = sqlite3_step(statement.statement);
|
||||
|
||||
if (SQLITE_BUSY == rc) {
|
||||
// this will happen if the db is locked, like if we are doing an update or insert.
|
||||
// in that case, retry the step... and maybe wait just 10 milliseconds.
|
||||
retry = YES;
|
||||
usleep(20);
|
||||
|
||||
if ([parentDB busyRetryTimeout] && (numberOfRetries++ > [parentDB busyRetryTimeout])) {
|
||||
|
||||
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [parentDB databasePath]);
|
||||
NSLog(@"Database busy");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
|
||||
// all is well, let's return.
|
||||
}
|
||||
else if (SQLITE_ERROR == rc) {
|
||||
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
|
||||
break;
|
||||
}
|
||||
else if (SQLITE_MISUSE == rc) {
|
||||
// uh oh.
|
||||
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// wtf?
|
||||
NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
|
||||
break;
|
||||
}
|
||||
|
||||
} while (retry);
|
||||
|
||||
|
||||
if (rc != SQLITE_ROW) {
|
||||
[self close];
|
||||
}
|
||||
|
||||
return (rc == SQLITE_ROW);
|
||||
}
|
||||
|
||||
- (BOOL) hasAnotherRow {
|
||||
return sqlite3_errcode([parentDB sqliteHandle]) == SQLITE_ROW;
|
||||
}
|
||||
|
||||
- (int) columnIndexForName:(NSString*)columnName {
|
||||
|
||||
if (!columnNamesSetup) {
|
||||
[self setupColumnNames];
|
||||
}
|
||||
|
||||
columnName = [columnName lowercaseString];
|
||||
|
||||
NSNumber *n = [columnNameToIndexMap objectForKey:columnName];
|
||||
|
||||
if (n) {
|
||||
return [n intValue];
|
||||
}
|
||||
|
||||
NSLog(@"Warning: I could not find the column named '%@'.", columnName);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (int) intForColumn:(NSString*)columnName {
|
||||
return [self intForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (int) intForColumnIndex:(int)columnIdx {
|
||||
return sqlite3_column_int(statement.statement, columnIdx);
|
||||
}
|
||||
|
||||
- (long) longForColumn:(NSString*)columnName {
|
||||
return [self longForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (long) longForColumnIndex:(int)columnIdx {
|
||||
return (long)sqlite3_column_int64(statement.statement, columnIdx);
|
||||
}
|
||||
|
||||
- (long long int) longLongIntForColumn:(NSString*)columnName {
|
||||
return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (long long int) longLongIntForColumnIndex:(int)columnIdx {
|
||||
return sqlite3_column_int64(statement.statement, columnIdx);
|
||||
}
|
||||
|
||||
- (BOOL) boolForColumn:(NSString*)columnName {
|
||||
return [self boolForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (BOOL) boolForColumnIndex:(int)columnIdx {
|
||||
return ([self intForColumnIndex:columnIdx] != 0);
|
||||
}
|
||||
|
||||
- (double) doubleForColumn:(NSString*)columnName {
|
||||
return [self doubleForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (double) doubleForColumnIndex:(int)columnIdx {
|
||||
return sqlite3_column_double(statement.statement, columnIdx);
|
||||
}
|
||||
|
||||
- (NSString*) stringForColumnIndex:(int)columnIdx {
|
||||
|
||||
if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx);
|
||||
|
||||
if (!c) {
|
||||
// null row.
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [NSString stringWithUTF8String:c];
|
||||
}
|
||||
|
||||
- (NSString*) stringForColumn:(NSString*)columnName {
|
||||
return [self stringForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (NSDate*) dateForColumn:(NSString*)columnName {
|
||||
return [self dateForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (NSDate*) dateForColumnIndex:(int)columnIdx {
|
||||
|
||||
if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]];
|
||||
}
|
||||
|
||||
|
||||
- (NSData*) dataForColumn:(NSString*)columnName {
|
||||
return [self dataForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (NSData*) dataForColumnIndex:(int)columnIdx {
|
||||
|
||||
if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
int dataSize = sqlite3_column_bytes(statement.statement, columnIdx);
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithLength:dataSize];
|
||||
|
||||
memcpy([data mutableBytes], sqlite3_column_blob(statement.statement, columnIdx), dataSize);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
- (NSData*) dataNoCopyForColumn:(NSString*)columnName {
|
||||
return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (NSData*) dataNoCopyForColumnIndex:(int)columnIdx {
|
||||
|
||||
if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
int dataSize = sqlite3_column_bytes(statement.statement, columnIdx);
|
||||
|
||||
NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob(statement.statement, columnIdx) length:dataSize freeWhenDone:NO];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) columnIndexIsNull:(int)columnIdx {
|
||||
return sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
- (BOOL) columnIsNull:(NSString*)columnName {
|
||||
return [self columnIndexIsNull:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
- (const unsigned char *) UTF8StringForColumnIndex:(int)columnIdx {
|
||||
|
||||
if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return sqlite3_column_text(statement.statement, columnIdx);
|
||||
}
|
||||
|
||||
- (const unsigned char *) UTF8StringForColumnName:(NSString*)columnName {
|
||||
return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]];
|
||||
}
|
||||
|
||||
|
||||
// returns autoreleased NSString containing the name of the column in the result set
|
||||
- (NSString*) columnNameForIndex:(int)columnIdx {
|
||||
return [NSString stringWithUTF8String: sqlite3_column_name(statement.statement, columnIdx)];
|
||||
}
|
||||
|
||||
- (void)setParentDB:(FMDatabase *)newDb {
|
||||
parentDB = newDb;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)query {
|
||||
return query;
|
||||
}
|
||||
|
||||
- (void)setQuery:(NSString *)value {
|
||||
[value retain];
|
||||
[query release];
|
||||
query = value;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)columnNameToIndexMap {
|
||||
return columnNameToIndexMap;
|
||||
}
|
||||
|
||||
- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value {
|
||||
[value retain];
|
||||
[columnNameToIndexMap release];
|
||||
columnNameToIndexMap = value;
|
||||
}
|
||||
|
||||
- (FMStatement *) statement {
|
||||
return statement;
|
||||
}
|
||||
|
||||
- (void)setStatement:(FMStatement *)value {
|
||||
if (statement != value) {
|
||||
[statement release];
|
||||
statement = [value retain];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
496
src/fmdb.m
Normal file
496
src/fmdb.m
Normal file
@ -0,0 +1,496 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FMDatabase.h"
|
||||
#import "FMDatabaseAdditions.h"
|
||||
|
||||
#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); return 123; } }
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// delete the old db.
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
[fileManager removeFileAtPath:@"/tmp/tmp.db" handler:nil];
|
||||
|
||||
FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
|
||||
if (![db open]) {
|
||||
NSLog(@"Could not open db.");
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// kind of experimentalish.
|
||||
[db setShouldCacheStatements:YES];
|
||||
|
||||
// create a bad statement, just to test the error code.
|
||||
[db executeUpdate:@"blah blah blah"];
|
||||
|
||||
FMDBQuickCheck([db hadError]);
|
||||
|
||||
if ([db hadError]) {
|
||||
NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
|
||||
}
|
||||
|
||||
// but of course, I don't bother checking the error codes below.
|
||||
// Bad programmer, no cookie.
|
||||
|
||||
[db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
|
||||
|
||||
|
||||
[db beginTransaction];
|
||||
int i = 0;
|
||||
while (i++ < 20) {
|
||||
[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
|
||||
@"hi'", // look! I put in a ', and I'm not escaping it!
|
||||
[NSString stringWithFormat:@"number %d", i],
|
||||
[NSNumber numberWithInt:i],
|
||||
[NSDate date],
|
||||
[NSNumber numberWithFloat:2.2f]];
|
||||
}
|
||||
[db commit];
|
||||
|
||||
|
||||
|
||||
// do it again, just because
|
||||
[db beginTransaction];
|
||||
i = 0;
|
||||
while (i++ < 20) {
|
||||
[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
|
||||
@"hi again'", // look! I put in a ', and I'm not escaping it!
|
||||
[NSString stringWithFormat:@"number %d", i],
|
||||
[NSNumber numberWithInt:i],
|
||||
[NSDate date],
|
||||
[NSNumber numberWithFloat:2.2f]];
|
||||
}
|
||||
[db commit];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
FMResultSet *rs = [db executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
|
||||
while ([rs next]) {
|
||||
// just print out what we've got in a number of formats.
|
||||
NSLog(@"%d %@ %@ %@ %@ %f %f",
|
||||
[rs intForColumn:@"c"],
|
||||
[rs stringForColumn:@"b"],
|
||||
[rs stringForColumn:@"a"],
|
||||
[rs stringForColumn:@"rowid"],
|
||||
[rs dateForColumn:@"d"],
|
||||
[rs doubleForColumn:@"d"],
|
||||
[rs doubleForColumn:@"e"]);
|
||||
|
||||
|
||||
if (!([[rs columnNameForIndex:0] isEqualToString:@"rowid"] &&
|
||||
[[rs columnNameForIndex:1] isEqualToString:@"a"])
|
||||
) {
|
||||
NSLog(@"WHOA THERE BUDDY, columnNameForIndex ISN'T WORKING!");
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
// close the result set.
|
||||
// it'll also close when it's dealloc'd, but we're closing the database before
|
||||
// the autorelease pool closes, so sqlite will complain about it.
|
||||
[rs close];
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// blob support.
|
||||
[db executeUpdate:@"create table blobTable (a text, b blob)"];
|
||||
|
||||
// let's read in an image from safari's app bundle.
|
||||
NSData *safariCompass = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"];
|
||||
if (safariCompass) {
|
||||
[db executeUpdate:@"insert into blobTable (a, b) values (?,?)", @"safari's compass", safariCompass];
|
||||
|
||||
rs = [db executeQuery:@"select b from blobTable where a = ?", @"safari's compass"];
|
||||
if ([rs next]) {
|
||||
safariCompass = [rs dataForColumn:@"b"];
|
||||
[safariCompass writeToFile:@"/tmp/compass.icns" atomically:NO];
|
||||
|
||||
// let's look at our fancy image that we just wrote out..
|
||||
system("/usr/bin/open /tmp/compass.icns");
|
||||
|
||||
// ye shall read the header for this function, or suffer the consequences.
|
||||
safariCompass = [rs dataNoCopyForColumn:@"b"];
|
||||
[safariCompass writeToFile:@"/tmp/compass_data_no_copy.icns" atomically:NO];
|
||||
system("/usr/bin/open /tmp/compass_data_no_copy.icns");
|
||||
}
|
||||
else {
|
||||
NSLog(@"Could not select image.");
|
||||
}
|
||||
|
||||
[rs close];
|
||||
|
||||
}
|
||||
else {
|
||||
NSLog(@"Can't find compass image..");
|
||||
}
|
||||
|
||||
|
||||
// test out the convenience methods in +Additions
|
||||
[db executeUpdate:@"create table t1 (a integer)"];
|
||||
[db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]];
|
||||
int a = [db intForQuery:@"select a from t1 where a = ?", [NSNumber numberWithInt:5]];
|
||||
if (a != 5) {
|
||||
NSLog(@"intForQuery didn't work (a != 5)");
|
||||
}
|
||||
|
||||
// test the busy rety timeout schtuff.
|
||||
|
||||
[db setBusyRetryTimeout:50000];
|
||||
|
||||
FMDatabase *newDb = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
|
||||
[newDb open];
|
||||
|
||||
rs = [newDb executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
|
||||
[rs next]; // just grab one... which will keep the db locked.
|
||||
|
||||
NSLog(@"Testing the busy timeout");
|
||||
|
||||
BOOL success = [db executeUpdate:@"insert into t1 values (5)"];
|
||||
|
||||
if (success) {
|
||||
NSLog(@"Whoa- the database didn't stay locked!");
|
||||
return 7;
|
||||
}
|
||||
else {
|
||||
NSLog(@"Hurray, our timeout worked");
|
||||
}
|
||||
|
||||
[rs close];
|
||||
[newDb close];
|
||||
|
||||
success = [db executeUpdate:@"insert into t1 values (5)"];
|
||||
if (!success) {
|
||||
NSLog(@"Whoa- the database shouldn't be locked!");
|
||||
return 8;
|
||||
}
|
||||
else {
|
||||
NSLog(@"Hurray, we can insert again!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test some nullness.
|
||||
[db executeUpdate:@"create table t2 (a integer, b integer)"];
|
||||
|
||||
if (![db executeUpdate:@"insert into t2 values (?, ?)", nil, [NSNumber numberWithInt:5]]) {
|
||||
NSLog(@"UH OH, can't insert a nil value for some reason...");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
rs = [db executeQuery:@"select * from t2"];
|
||||
while ([rs next]) {
|
||||
NSString *a = [rs stringForColumnIndex:0];
|
||||
NSString *b = [rs stringForColumnIndex:1];
|
||||
|
||||
if (a != nil) {
|
||||
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
|
||||
NSLog(@"OH OH, PROBLEMO!");
|
||||
return 10;
|
||||
}
|
||||
else {
|
||||
NSLog(@"YAY, NULL VALUES");
|
||||
}
|
||||
|
||||
if (![b isEqualToString:@"5"]) {
|
||||
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
|
||||
NSLog(@"OH OH, PROBLEMO!");
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// test some inner loop funkness.
|
||||
[db executeUpdate:@"create table t3 (a somevalue)"];
|
||||
|
||||
|
||||
// do it again, just because
|
||||
[db beginTransaction];
|
||||
i = 0;
|
||||
while (i++ < 20) {
|
||||
[db executeUpdate:@"insert into t3 (a) values (?)" , [NSNumber numberWithInt:i]];
|
||||
}
|
||||
[db commit];
|
||||
|
||||
|
||||
|
||||
|
||||
rs = [db executeQuery:@"select * from t3"];
|
||||
while ([rs next]) {
|
||||
int foo = [rs intForColumnIndex:0];
|
||||
|
||||
int newVal = foo + 100;
|
||||
|
||||
[db executeUpdate:@"update t3 set a = ? where a = ?" , [NSNumber numberWithInt:newVal], [NSNumber numberWithInt:foo]];
|
||||
|
||||
|
||||
FMResultSet *rs2 = [db executeQuery:@"select a from t3 where a = ?", [NSNumber numberWithInt:newVal]];
|
||||
[rs2 next];
|
||||
|
||||
if ([rs2 intForColumnIndex:0] != newVal) {
|
||||
NSLog(@"Oh crap, our update didn't work out!");
|
||||
return 9;
|
||||
}
|
||||
|
||||
[rs2 close];
|
||||
}
|
||||
|
||||
|
||||
// NSNull tests
|
||||
[db executeUpdate:@"create table nulltest (a text, b text)"];
|
||||
|
||||
[db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , [NSNull null], @"a"];
|
||||
[db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , nil, @"b"];
|
||||
|
||||
rs = [db executeQuery:@"select * from nulltest"];
|
||||
|
||||
while ([rs next]) {
|
||||
|
||||
NSString *a = [rs stringForColumnIndex:0];
|
||||
NSString *b = [rs stringForColumnIndex:1];
|
||||
|
||||
if (!b) {
|
||||
NSLog(@"Oh crap, the nil / null inserts didn't work!");
|
||||
return 10;
|
||||
}
|
||||
|
||||
if (a) {
|
||||
NSLog(@"Oh crap, the nil / null inserts didn't work (son of error message)!");
|
||||
return 11;
|
||||
}
|
||||
else {
|
||||
NSLog(@"HURRAH FOR NSNULL (and nil)!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// null dates
|
||||
|
||||
NSDate *date = [NSDate date];
|
||||
[db executeUpdate:@"create table datetest (a double, b double, c double)"];
|
||||
[db executeUpdate:@"insert into datetest (a, b, c) values (?, ?, 0)" , [NSNull null], date];
|
||||
|
||||
rs = [db executeQuery:@"select * from datetest"];
|
||||
|
||||
while ([rs next]) {
|
||||
|
||||
NSDate *a = [rs dateForColumnIndex:0];
|
||||
NSDate *b = [rs dateForColumnIndex:1];
|
||||
NSDate *c = [rs dateForColumnIndex:2];
|
||||
|
||||
if (a) {
|
||||
NSLog(@"Oh crap, the null date insert didn't work!");
|
||||
return 12;
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
NSLog(@"Oh crap, the 0 date insert didn't work!");
|
||||
return 12;
|
||||
}
|
||||
|
||||
NSTimeInterval dti = fabs([b timeIntervalSinceDate:date]);
|
||||
|
||||
if (floor(dti) > 0.0) {
|
||||
NSLog(@"Date matches didn't really happen... time difference of %f", dti);
|
||||
return 13;
|
||||
}
|
||||
|
||||
|
||||
dti = fabs([c timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:0]]);
|
||||
|
||||
if (floor(dti) > 0.0) {
|
||||
NSLog(@"Date matches didn't really happen... time difference of %f", dti);
|
||||
return 13;
|
||||
}
|
||||
}
|
||||
|
||||
NSDate *foo = [db dateForQuery:@"select b from datetest where c = 0"];
|
||||
assert(foo);
|
||||
NSTimeInterval dti = fabs([foo timeIntervalSinceDate:date]);
|
||||
if (floor(dti) > 0.0) {
|
||||
NSLog(@"Date matches didn't really happen... time difference of %f", dti);
|
||||
return 14;
|
||||
}
|
||||
|
||||
[db executeUpdate:@"create table nulltest2 (s text, d data, i integer, f double, b integer)"];
|
||||
|
||||
[db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , @"Hi", safariCompass, [NSNumber numberWithInt:12], [NSNumber numberWithFloat:4.4], [NSNumber numberWithBool:YES]];
|
||||
[db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , nil, nil, nil, nil, [NSNull null]];
|
||||
|
||||
rs = [db executeQuery:@"select * from nulltest2"];
|
||||
|
||||
while ([rs next]) {
|
||||
|
||||
int i = [rs intForColumnIndex:2];
|
||||
|
||||
if (i == 12) {
|
||||
// it's the first row we inserted.
|
||||
FMDBQuickCheck(![rs columnIndexIsNull:0]);
|
||||
FMDBQuickCheck(![rs columnIndexIsNull:1]);
|
||||
FMDBQuickCheck(![rs columnIndexIsNull:2]);
|
||||
FMDBQuickCheck(![rs columnIndexIsNull:3]);
|
||||
FMDBQuickCheck(![rs columnIndexIsNull:4]);
|
||||
FMDBQuickCheck( [rs columnIndexIsNull:5]);
|
||||
|
||||
FMDBQuickCheck([[rs dataForColumn:@"d"] length] == [safariCompass length]);
|
||||
FMDBQuickCheck(![rs dataForColumn:@"notthere"]);
|
||||
FMDBQuickCheck(![rs stringForColumnIndex:-2]);
|
||||
FMDBQuickCheck([rs boolForColumnIndex:4]);
|
||||
FMDBQuickCheck([rs boolForColumn:@"b"]);
|
||||
|
||||
FMDBQuickCheck(fabs(4.4 - [rs doubleForColumn:@"f"]) < 0.0000001);
|
||||
|
||||
FMDBQuickCheck(12 == [rs intForColumn:@"i"]);
|
||||
FMDBQuickCheck(12 == [rs intForColumnIndex:2]);
|
||||
|
||||
FMDBQuickCheck(0 == [rs intForColumnIndex:12]); // there is no 12
|
||||
FMDBQuickCheck(0 == [rs intForColumn:@"notthere"]);
|
||||
|
||||
FMDBQuickCheck(12 == [rs longForColumn:@"i"]);
|
||||
FMDBQuickCheck(12 == [rs longLongIntForColumn:@"i"]);
|
||||
}
|
||||
else {
|
||||
// let's test various null things.
|
||||
|
||||
FMDBQuickCheck([rs columnIndexIsNull:0]);
|
||||
FMDBQuickCheck([rs columnIndexIsNull:1]);
|
||||
FMDBQuickCheck([rs columnIndexIsNull:2]);
|
||||
FMDBQuickCheck([rs columnIndexIsNull:3]);
|
||||
FMDBQuickCheck([rs columnIndexIsNull:4]);
|
||||
FMDBQuickCheck([rs columnIndexIsNull:5]);
|
||||
|
||||
|
||||
FMDBQuickCheck(![rs dataForColumn:@"d"]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
[db executeUpdate:@"create table testOneHundredTwelvePointTwo (a text, b integer)"];
|
||||
[db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:2], nil]];
|
||||
[db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:3], nil]];
|
||||
|
||||
|
||||
rs = [db executeQuery:@"select * from testOneHundredTwelvePointTwo where b > ?" withArgumentsInArray:[NSArray arrayWithObject:[NSNumber numberWithInteger:1]]];
|
||||
|
||||
FMDBQuickCheck([rs next]);
|
||||
|
||||
FMDBQuickCheck([rs hasAnotherRow]);
|
||||
FMDBQuickCheck(![db hadError]);
|
||||
|
||||
FMDBQuickCheck([[rs stringForColumnIndex:0] isEqualToString:@"one"]);
|
||||
FMDBQuickCheck([rs intForColumnIndex:1] == 2);
|
||||
|
||||
FMDBQuickCheck([rs next]);
|
||||
|
||||
FMDBQuickCheck([rs intForColumnIndex:1] == 3);
|
||||
|
||||
FMDBQuickCheck(![rs next]);
|
||||
FMDBQuickCheck(![rs hasAnotherRow]);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)"]);
|
||||
FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)", @"one", @"two"]));
|
||||
|
||||
rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;"];
|
||||
|
||||
FMDBQuickCheck((rs != nil));
|
||||
|
||||
[rs next];
|
||||
|
||||
FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
|
||||
FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
|
||||
|
||||
FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
|
||||
|
||||
[rs close];
|
||||
|
||||
// let's try these again, with the withArgumentsInArray: variation
|
||||
FMDBQuickCheck([db executeUpdate:@"drop table t4;" withArgumentsInArray:[NSArray array]]);
|
||||
FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)" withArgumentsInArray:[NSArray array]]);
|
||||
FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", @"two", nil]]));
|
||||
|
||||
rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;" withArgumentsInArray:[NSArray array]];
|
||||
|
||||
FMDBQuickCheck((rs != nil));
|
||||
|
||||
[rs next];
|
||||
|
||||
FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
|
||||
FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
|
||||
|
||||
FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
|
||||
|
||||
[rs close];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
FMDBQuickCheck([db tableExists:@"t4"]);
|
||||
FMDBQuickCheck(![db tableExists:@"thisdoesntexist"]);
|
||||
|
||||
rs = [db getSchema];
|
||||
while ([rs next]) {
|
||||
FMDBQuickCheck([[rs stringForColumn:@"type"] isEqualToString:@"table"]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// just for fun.
|
||||
rs = [db executeQuery:@"PRAGMA database_list"];
|
||||
while ([rs next]) {
|
||||
NSString *file = [rs stringForColumn:@"file"];
|
||||
NSLog(@"database_list: %@", file);
|
||||
}
|
||||
|
||||
|
||||
// print out some stats if we are using cached statements.
|
||||
if ([db shouldCacheStatements]) {
|
||||
|
||||
NSEnumerator *e = [[db cachedStatements] objectEnumerator];;
|
||||
FMStatement *statement;
|
||||
|
||||
while ((statement = [e nextObject])) {
|
||||
NSLog(@"%@", statement);
|
||||
}
|
||||
}
|
||||
NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);
|
||||
|
||||
|
||||
[db close];
|
||||
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user