blob: 0daa02ce8e432e166903b1813f38b4872a78b70b [file] [log] [blame]
//
// GTMSQLite.h
//
// Copyright 2006-2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//
//
// This class is a convenience wrapper for SQLite storage with
// release/retain semantics. In its most basic form, that is all this
// class offers. You have the option of activating "CFAdditions" on
// init which patches or overrides the following SQLite functionality:
//
// - Strings you pass through the API layer will always be converted
// to precomposed UTF-8 with compatibility mapping
// (kCFStringNormalizationFormKC). This is done in an attempt to
// make SQLite correctly handle string equality for composed
// character sequences. This change applies only to
// NSStrings/CFStrings passed through the GTMSQLiteDatabase or
// GTMSQLiteStatement. Direct access to the database using the
// underlying sqlite3_* handles is not affected.
//
// - The SQL UPPER/LOWER functions are replaced with CFString-based
// implementations which (unlike SQLite's native implementation)
// handle case conversion outside the ASCII range. These
// implementations seem to be 20-30% slower than the SQLite
// implementations but may be worth it for accuracy.
//
// - The SQLite "NOCASE" collation is replaced with a CFString-based
// collation that is case insensitive but still uses literal
// comparison (composition-sensitive).
//
// - Additional collation sequences can be created by using these keywords
// separated by underscores. Each option corresponds to a CFStringCompareFlags
// option.
// NOCASE (kCFCompareCaseInsensitive)
// NONLITERAL (kCFCompareNonliteral)
// LOCALIZED (kCFCompareLocalized)
// NUMERIC (kCFCompareNumerically)
// These additional options are available when linking with the 10.5 SDK:
// NODIACRITIC (kCFCompareDiacriticInsensitive)
// WIDTHINSENSITIVE (kCFCompareWidthInsensitive)
//
// Ordering of the above options can be changed by adding "REVERSE".
//
// Thus, for a case-insensitive, width-insensitive, composition-insensitive
// comparison that ignores diacritical marks and sorts in reverse use:
//
// NOCASE_NONLITERAL_NODIACRITIC_WIDTHINSENSITIVE_REVERSE
//
// - SQL LIKE and GLOB commands are implemented with CFString/CFCharacterSet
// comparisons. As with the other CF additions, this gives us better handling
// of case and composed character sequences. However, whereever reasonable,
// SQLite semantics have been retained. Specific notes:
//
// * LIKE is case insensitive and uses non-literal comparison
// (kCFCompareNonliteral) by default. It is possible to modify this
// behavior using the accessor methods. You must use those methods
// instead of the SQLite "PRAGMA case_sensitive_like" in order for them
// to interact properly with our CFString implementations.
//
// * ESCAPE clauses to LIKE are honored, but the escape character must
// be expressable as a single UniChar (UTF16). The escaped characters in
// LIKE only escape the following UniChar, not a composed character
// sequence. This is not viewed as a limitation since the use of ESCAPE
// is typically only for characters with meaning to SQL LIKE ('%', '_')
// all of which can be expressed as a single UniChar.
//
// * GLOB is by default case sensitive but non-literal. Again, accessor
// methods are available to change this behavior.
//
// * Single character pattern matches ('_' for LIKE, '?' for GLOB) will
// always consume a full composed character sequence.
//
// * As with the standard SQLite implementation, character set comparisons
// are only available for GLOB.
//
// * Character set comparisons are always literal and case sensitive and do
// not take into account composed character sequences. Essentially
// character sets should always be expressed as a set of single UniChars
// or ranges between single UniChars.
//
#import <Foundation/Foundation.h>
#import <sqlite3.h>
/// Wrapper for SQLite with release/retain semantics and CFString convenience features
@interface GTMSQLiteDatabase : NSObject {
@protected
sqlite3 *db_; // strong
NSString *path_; // strong
int timeoutMS_;
BOOL hasCFAdditions_;
CFOptionFlags likeOptions_;
CFOptionFlags globOptions_;
NSMutableArray *userArgDataPool_; // strong
}
// Get the numeric version number of the SQLite library (compiled in value
// for SQLITE_VERSION_NUMBER).
//
// Returns:
// Integer version number
//
+ (int)sqliteVersionNumber;
// Get the string version number of the SQLite library.
//
// Returns:
// Autoreleased NSString version string
//
+ (NSString *)sqliteVersionString;
// Create and open a database instance on a file-based database.
//
// Args:
// path: Path to the database. If it does not exist an empty database
// will be created.
// withCFAdditions: If true, the SQLite database will include CFString
// based string functions and collation sequences. See
// the class header for information on these differences
// and performance impact.
// err: Result code from SQLite. If nil is returned by this function
// check the result code for the error. If NULL no result code is
// reported.
//
- (id)initWithPath:(NSString *)path
withCFAdditions:(BOOL)additions
utf8:(BOOL)useUTF8
errorCode:(int *)err;
// Create and open a memory-based database. Memory-based databases
// cannot be shared amongst threads, and each instance is unique. See
// SQLite documentation for details.
//
// For argument details see [... initWithPath:withCFAdditions:errorCode:]
//
- (id)initInMemoryWithCFAdditions:(BOOL)additions
utf8:(BOOL)useUTF8
errorCode:(int *)err;
// Get the underlying SQLite database handle. In general you should
// never do this, if you do use this be careful with how you compose
// and decompse strings you pass to the database.
//
// Returns:
// sqlite3 pointer
//
- (sqlite3 *)sqlite3DB;
// Enable/Disable the database synchronous mode. Disabling
// synchronous mode results in much faster insert throughput at the
// cost of safety. See the SQlite documentation for details.
//
// Args:
// enable: Boolean flag to determine mode.
//
- (void)synchronousMode:(BOOL)enable;
// Check if this database instance has our CFString functions and collation
// sequences (see top of file for details).
//
// Returns:
// YES if the GTMSQLiteDatabase instance has our CF additions
//
- (BOOL)hasCFAdditions;
// Set comparison options for the "LIKE" operator for databases with
// our CF addtions active.
//
// Args:
// options: CFStringCompareFlags value. Note that a limited list
// of options are supported. For example one cannot
// use kCFCompareBackwards as an option.
//
- (void)setLikeComparisonOptions:(CFOptionFlags)options;
// Get current comparison options for the "LIKE" operator in a database
// with our CF additions active.
//
// Returns:
// Current comparison options or zero if CF additions are inactive.
//
- (CFOptionFlags)likeComparisonOptions;
// Set comparison options for the "GLOB" operator for databases with
// our CF addtions active.
//
// Args:
// options: CFStringCompareFlags value. Note that a limited list
// of options are supported. For example one cannot
// use kCFCompareBackwards as an option.
//
- (void)setGlobComparisonOptions:(CFOptionFlags)options;
// Get current comparison options for the "GLOB" operator in a database
// with our CF additions active.
//
// Returns:
// Current comparison options or zero if CF additions are inactive.
//
- (CFOptionFlags)globComparisonOptions;
// Obtain the last error code from the database
//
// Returns:
// SQLite error code, if no error is pending returns SQLITE_OK
//
- (int)lastErrorCode;
// Obtain an error string for the last error from the database
//
// Returns:
// Autoreleased NSString error message
//
- (NSString *)lastErrorString;
// Obtain a count of rows added, mmodified or deleted by the most recent
// statement. See sqlite3_changes() for details and limitations.
//
// Returns:
// Row count
//
- (int)lastChangeCount;
// Obtain a count of rows added, mmodified or deleted since the database
// was opened. See sqlite3_total_changes() for details and limitations.
//
// Returns:
// Row count
//
- (int)totalChangeCount;
// Obtain the last insert row ID
//
// Returns:
// 64-bit row ID
//
- (unsigned long long)lastInsertRowID;
// Interrupt any currently running database operations as soon as possible.
// Running operations will receive a SQLITE_INTERRUPT and will need to
// handle it correctly (this is the callers problem to deal with).
//
- (void)interrupt;
// Set the timeout value in milliseconds. This is a database global affecting
// all running and future statements.
//
// Args:
// timeoutMS: Integer count in ms SQLite will wait for the database to
// unlock before giving up and returning SQLITE_BUSY. A value
// of 0 or less means the database always returns immediately.
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)setBusyTimeoutMS:(int)timeoutMS;
// Get the current busy timeout in milliseconds.
//
// Returns:
// Current database busy timeout value in ms, 0 or less means no timeout.
//
- (int)busyTimeoutMS;
// Execute a string containing one or more SQL statements. No returned data
// is available, use GTMSQLiteStatement for that usage.
//
// Args:
// sql: Raw SQL statement to prepare. It is the caller's responsibility
// to properly escape the SQL.
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)executeSQL:(NSString *)sql;
// Convenience method to start a deferred transaction (most common case).
//
// Returns:
// YES if the transaction started successfully
//
- (BOOL)beginDeferredTransaction;
// Convenience method to roll back a transaction.
//
// Returns:
// YES if the transaction rolled back successfully
//
- (BOOL)rollback;
// Convenience method to commit a transaction.
//
// Returns:
// YES if the transaction committed successfully
//
- (BOOL)commit;
@end
// Wrapper class for SQLite statements with retain/release semantics.
// Attempts to behave like an NSEnumerator, however you should bind
// your values before beginning enumeration and unlike NSEnumerator,
// a reset is supported.
//
// The GTMSQLiteDatabase class has options to modify some SQL
// functions and force particular string representations. This class
// honors the database preferences for those options. See the
// GTMSQLiteDatabase header for details.
/// Wrapper class for SQLite statements with retain/release
/// semantics.
@interface GTMSQLiteStatement : NSObject {
@protected
sqlite3_stmt *statement_;
BOOL hasCFAdditions_;
}
#pragma mark Creation, Access and Finalization
// Create an autoreleased prepared statement, see initWithSQL: for arguments.
//
// NOTE: Even though this object is autoreleased you MUST call
// [finalizeStatement] on this when your done. See the init for explanation.
//
// Returns:
// Autoreleased GTMSQLiteStatement
//
+ (id)statementWithSQL:(NSString *)sql
inDatabase:(GTMSQLiteDatabase *)gtmdb
errorCode:(int *)err;
// Designated initializer, create a prepared statement. Positional and named
// parameters are supported, see the SQLite documentation.
//
// NOTE: Although this object will clean up its statement when deallocated,
// you are REQUIRED to "finalize" the statement when you are
// through with it. Failing to do this will prevent the database from allowing
// new transactions or queries. In other words, leaving an instance on the
// autorelease pool unfinalized may interfere with other database usage if any
// caller sharing the database uses transactions.
//
// Args:
// sql: Raw SQL statement to prepare. It is the caller's responsibility
// to properly escape the SQL and make sure that the SQL contains
// only _one_ statement. Additional statements are silently ignored.
// db: The GTMSQLiteDatabase (not retained)
// err: Result code from SQLite. If nil is returned by this function
// check the result code for the error. If NULL no result code is
// reported.
//
- (id)initWithSQL:(NSString *)sql
inDatabase:(GTMSQLiteDatabase *)gtmdb
errorCode:(int *)err;
// Get the underlying SQLite statement handle. In general you should never
// do this, if you do use this be careful with how you compose and
// decompse strings you pass to the database.
//
// Returns:
// sqlite3_stmt pointer
//
- (sqlite3_stmt *)sqlite3Statement;
// Finalize the statement, allowing other transactions to start on the database
// This method MUST be called when you are done with a statement. Failure to
// do so means that the database will not be torn down properly when it's
// retain count drops to 0 or GC collects it.
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)finalizeStatement;
#pragma mark Parameters and Binding
// Get the number of parameters that can be bound in the prepared statement.
//
// Returns:
// Integer count of parameters or -1 on error
//
- (int)parameterCount;
// Get the position of a parameter with a given name.
//
// Args:
// paramName: String name of the parameter, including any leading punctuation
// (see SQLite docs)
//
// Returns:
// 1-based parameter position index or -1 on error
//
- (int)positionOfParameterNamed:(NSString *)paramName;
// Get the name of a parameter at a particular index.
//
// Args:
// position: Parameter position (1-based index)
//
// Returns:
// Autoreleased string name of the parameter, including any leading
// punctuation (see SQLite docs) or nil on error.
//
- (NSString *)nameOfParameterAtPosition:(int)position;
// Bind a NULL at a given position
//
// Args:
// position: Parameter position (1-based index)
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindSQLNullAtPosition:(int)position;
// Bind a blob parameter at a given position index to a raw pointer and
// length. The data will be copied by SQLite
//
// Args:
// position: Parameter position (1-based index)
// bytes: Raw pointer to the data to copy/bind
// length: Number of bytes in the blob
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindBlobAtPosition:(int)position bytes:(void *)bytes length:(int)length;
// Bind an NSData as a blob at a given position. The data will be copied
// by SQLite.
//
// Args:
// position: Parameter position (1-based index)
// data: NSData to convert to blob
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindBlobAtPosition:(int)position data:(NSData *)data;
// Bind a double at the given position (for floats convert to double).
//
// Args:
// position: Parameter position (1-based index)
// value: Double to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindDoubleAtPosition:(int)position value:(double)value;
// Bind an NSNumber as a double value at the given position.
//
// Args:
// position: Parameter position (1-based index)
// number: NSNumber to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindNumberAsDoubleAtPosition:(int)position number:(NSNumber *)number;
// Bind a 32-bit integer at the given position.
//
// Args:
// position: Parameter position (1-based index)
// value: Integer to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindInt32AtPosition:(int)position value:(int)value;
// Bind an NSNumber as a 32-bit integer value at the given position.
//
// Args:
// position: Parameter position (1-based index)
// number: NSNumber to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindNumberAsInt32AtPosition:(int)position number:(NSNumber *)number;
// Bind a 64-bit integer at the given position.
//
// Args:
// position: Parameter position (1-based index)
// value: Int64 value to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindLongLongAtPosition:(int)position value:(long long)value;
// Bind an NSNumber as a 64-bit integer value at the given position.
//
// Args:
// position: Parameter position (1-based index)
// number: NSNumber to bind
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindNumberAsLongLongAtPosition:(int)position number:(NSNumber *)number;
// Bind a string at the given position.
//
// Args:
// position: Parameter position (1-based index)
// string: String to bind (string will be converted to UTF8 and copied).
// NOTE: For bindings it is not necessary for you to SQL escape
// your strings.
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)bindStringAtPosition:(int)position string:(NSString *)string;
#pragma mark Results
// Get the number of result columns per row this statement will generate.
//
// Returns:
// Column count, 0 if no columns will be returned ("UPDATE.." etc.),
// -1 on error.
//
- (int)resultColumnCount;
// Get the name of result colument at a given index.
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Autoreleased NSString column name or nil if no column exists at that
// position or error.
//
- (NSString *)resultColumnNameAtPosition:(int)position;
// Get the number of data values in the current row of this statement.
// Generally this will be the same as resultColumnCount:, except when row
// iteration is done (see SQLite docs for sqlite3_data_count()).
//
// Returns:
// Data count or 0 if no data will be returned, -1 on error.
//
- (int)rowDataCount;
// Get the SQLite type constant for a column in a row. Note that because
// SQLite does not enforce column type restrictions the type of a particular
// column in a row may not match the declared type of the column.
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// SQLite data type constant (i.e. SQLITE_INTEGER, SQLITE_FLOAT, etc.) or
// -1 on error.
//
- (int)resultColumnTypeAtPosition:(int)position;
// Get the data for a result row blob column as an NSData
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Autoreleased NSData, nil on error
//
- (NSData *)resultBlobDataAtPosition:(int)position;
// Get the data for a result row blob column as a double
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Double value
//
- (double)resultDoubleAtPosition:(int)position;
// Get the data for a result row blob column as an integer
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Integer value
//
- (int)resultInt32AtPosition:(int)position;
// Get the data for a result row blob column as a long long
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Long long value
//
- (long long)resultLongLongAtPosition:(int)position;
// Get the data for a result row blob column as an NSNumber
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Autoreleased NSNumber value or nil on error
//
- (NSNumber *)resultNumberAtPosition:(int)position;
// Get the data for a result row blob column as an NSString
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Autoreleased NSString value or nil on error
//
- (NSString *)resultStringAtPosition:(int)position;
// Get a Foundation object (NSData, NSNumber, NSString, NSNull) for the column,
// autodetecting the most appropriate representation.
//
// Args:
// position: Column position (0-based index)
//
// Returns:
// Autoreleased Foundation type, nil on error
//
- (id)resultFoundationObjectAtPosition:(int)position;
// Get an array of Foundation objects for the row in query column order.
//
// Returns:
// Autoreleased array of Foundation types or nil if there is no
// data in the row or error
//
- (NSArray *)resultRowArray;
// Get a dictionary of Foundation objects for the row keyed by column name.
//
// Returns:
// Autoreleased dictionary of Foundation types or nil if there is no
// data in the row or error.
//
- (NSDictionary *)resultRowDictionary;
#pragma mark Rows
// Step the statement forward one row, potentially spinning forever till
// the row can be located (if database is SQLITE_BUSY).
//
// Returns:
// SQLite result code, SQLITE_ROW if a row was found or SQLITE_DONE if
// no further rows match the statement.
//
- (int)stepRow;
// Step the statement forward one row, waiting at most the currrent database
// busy timeout (see [GTMSQLiteDatabase setBusyTimeoutMS]).
//
// Returns:
// SQLite result code, SQLITE_ROW if a row was found or SQLITE_DONE if
// no further rows match the statement. If SQLITE_BUSY is returned the
// database did not unlock during the timeout.
//
- (int)stepRowWithTimeout;
// Reset the statement starting again at the first row
//
// Returns:
// SQLite result code, SQLITE_OK on no error
//
- (int)reset;
// Check if the SQLite parser recognizes the receiver as one or more valid
// SQLite statements.
//
// Returns:
// YES if the string is a complete and valid SQLite statement
//
+ (BOOL)isCompleteStatement:(NSString *)string;
// Quote and escape the receiver for SQL.
// Example: "This is wild! It's fun!"
// Becomes: "'This is wild! It''s fun!'"
//
// Returns:
// Autoreleased NSString
+ (NSString *)quoteAndEscapeString:(NSString *)string;
@end