├── Classes ├── PLDatabase.h ├── PLDatabaseConnectionProvider.h ├── PLDatabaseConstants.h ├── PLDatabaseFilterConnectionProvider.h ├── PLDatabaseFilterConnectionProvider.m ├── PLDatabaseFilterConnectionProviderTests.m ├── PLDatabaseMigrationConnectionProvider.h ├── PLDatabaseMigrationConnectionProvider.m ├── PLDatabaseMigrationConnectionProviderTests.m ├── PLDatabaseMigrationDelegate.h ├── PLDatabaseMigrationManager.h ├── PLDatabaseMigrationManager.m ├── PLDatabaseMigrationManagerTests.m ├── PLDatabaseMigrationTransactionManager.h ├── PLDatabaseMigrationVersionManager.h ├── PLDatabasePoolConnectionProvider.h ├── PLDatabasePoolConnectionProvider.m ├── PLDatabasePoolConnectionProviderTests.m ├── PLPreparedStatement.h ├── PLResultSet.h ├── PLSqliteConnectionProvider.h ├── PLSqliteConnectionProvider.m ├── PLSqliteConnectionProviderTests.m ├── PLSqliteDatabase.h ├── PLSqliteDatabase.m ├── PLSqliteDatabaseTests.m ├── PLSqliteMigrationManager.h ├── PLSqliteMigrationManager.m ├── PLSqliteMigrationManagerTests.m ├── PLSqlitePreparedStatement.h ├── PLSqlitePreparedStatement.m ├── PLSqlitePreparedStatementTests.m ├── PLSqliteResultSet.h ├── PLSqliteResultSet.m ├── PLSqliteResultSetTests.m ├── PLSqliteStatementCache.h ├── PLSqliteStatementCache.m ├── PLSqliteStatementCacheTests.m ├── PLSqliteUnlockNotify.h ├── PLSqliteUnlockNotify.m ├── PlausibleDatabase.h ├── PlausibleDatabase.m └── PlausibleDatabaseTests.m ├── Dependencies ├── README.txt └── SQLite │ ├── Makefile │ ├── libplsqlite3-ios.a │ ├── libplsqlite3-macosx.a │ ├── sqlite3.c │ ├── sqlite3.h │ └── sqlite3ext.h ├── Doxyfile ├── LICENSE ├── Other Sources └── Prefix.h ├── PlausibleDatabase.xcodeproj ├── TemplateIcon.icns └── project.pbxproj ├── README.md └── Resources ├── PlausibleDatabase-Info.plist └── Tests-Info.plist /Classes/PLDatabase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLDatabaseConstants.h" 33 | #import "PLPreparedStatement.h" 34 | #import "PLResultSet.h" 35 | 36 | /** 37 | * Standard SQL transaction isolation levels. These levels define the minimum isolation required; a database 38 | * is free to apply stricter isolation than has been requested. 39 | * 40 | * @ingroup enums 41 | */ 42 | typedef enum { 43 | /** Statements can read rows that have been modified by other transactions and have not yet been committed. */ 44 | PLDatabaseIsolationLevelReadUncommitted = 0, 45 | 46 | /** Statements cannot read changes that have not been committed by other transactions. Changes that 47 | * have been comitted will be readable. */ 48 | PLDatabaseIsolationLevelReadCommitted = 1, 49 | 50 | /** Statements cannot read changes that have not been committed by other transactions, and no other transactions 51 | * may modify data that has been read by the current transaction until the current transaction is completed. */ 52 | PLDatabaseIsolationLevelRepeatableRead = 2, 53 | 54 | /** 55 | * Statements cannot read changes that have not been committed by other transactions, no other transactions may 56 | * modify data that has been read by the current transaction until the current transaction is completed, and 57 | * other transactions cannot insert new rows with values that would fall into the range of rows read by any 58 | * statement in the current transaction until the current transaction complets. 59 | */ 60 | PLDatabaseIsolationLevelSerializable = 3 61 | } PLDatabaseIsolationLevel; 62 | 63 | typedef enum { 64 | /** Request that the transaction be committed. */ 65 | PLDatabaseTransactionCommit = 0, 66 | 67 | /** Request that the transaction be rolled back. The transaction will be automatically retried if the immediate 68 | * previous database failure was caused by a deadlock condition. Return PLDatabaseTransactionRollbackDisableRetry to 69 | * prevent retry behavior. */ 70 | PLDatabaseTransactionRollback = 1, 71 | 72 | /** Request that the transaction be rolled back. It will not be retried. */ 73 | PLDatabaseTransactionRollbackDisableRetry = 2 74 | } PLDatabaseTransactionResult; 75 | 76 | /** 77 | * Protocol for interacting with an SQL database. 78 | * 79 | * @par Object Types 80 | * All drivers support conversion to and from the following object types: 81 | * - NSString 82 | * - NSNumber 83 | * - NSData 84 | * 85 | * @par Scalar Types 86 | * All drivers implement conversion to and from the scalar types as defined in 87 | * the Key Value Coding documentation, Scalar and Structure Support: 88 | * http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/DataTypes.html#//apple_ref/doc/uid/20002171-184842-BCIJIBHC 89 | * 90 | * @par 91 | * The mapping of these scalar types to specific database types is implementation 92 | * defined. Refer to the database driver's documentation for the specific mapping 93 | * used. 94 | * 95 | * @par Thread Safety 96 | * PLDatabase instances implement no locking and must not be shared between threads 97 | * without external synchronization. 98 | */ 99 | @protocol PLDatabase 100 | 101 | /** 102 | * Test that the connection is active. 103 | */ 104 | - (BOOL) goodConnection; 105 | 106 | /** 107 | * Close the database connection, releasing any held database resources. 108 | * After calling, no further PLDatabase methods may be called on the instance. 109 | * 110 | * As PLDatabase objects may be placed into autorelease pools, with indeterminate 111 | * release of database resources, this method should be used to ensure that the database 112 | * connection is closed in a timely manner. 113 | * 114 | * Failure to call close will not result in any resource leaks, but may result in 115 | * database connections unexpectedly remaining open, especially in a garbage collection 116 | * environment. 117 | */ 118 | - (void) close; 119 | 120 | 121 | /** 122 | * Prepare and return a new PLPreparedStatement. 123 | * 124 | * @param statement SQL statement to prepare. 125 | * @return The prepared statement, or nil if it could not be prepared. 126 | */ 127 | - (id) prepareStatement: (NSString *) statement; 128 | 129 | /** 130 | * Prepare and return a new PLPreparedStatement. 131 | * 132 | * @param statement SQL statement to prepare. 133 | * @param outError A pointer to an NSError object variable. If an error occurs, this 134 | * pointer will contain an error object indicating why the statement could not be prepared. 135 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 136 | * parameter, and no error information will be provided. 137 | * @return The prepared statement, or nil if it could not be prepared. 138 | */ 139 | - (id) prepareStatement: (NSString *) statement error: (NSError **) outError; 140 | 141 | 142 | /** 143 | * Execute an update, returning YES on success, NO on failure. 144 | * 145 | * Any arguments should be provided following the statement, and 146 | * referred to using standard '?' JDBC substitutions 147 | * 148 | * @param statement SQL statement to execute. 149 | */ 150 | - (BOOL) executeUpdate: (NSString *) statement, ...; 151 | 152 | /** 153 | * Execute an update, returning YES on success, NO on failure. 154 | * 155 | * Any arguments should be provided following the statement, and 156 | * referred to using standard '?' JDBC substitutions 157 | * 158 | * @param error A pointer to an NSError object variable. If an error occurs, this 159 | * pointer will contain an error object indicating why the statement could not be executed. 160 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 161 | * parameter, and no error information will be provided. 162 | * @param statement SQL statement to execute. 163 | * 164 | */ 165 | - (BOOL) executeUpdateAndReturnError: (NSError **) error statement: (NSString *) statement, ...; 166 | 167 | /** 168 | * Execute a query, returning a PLResultSet. 169 | * 170 | * Any arguments should be provided following the statement, and 171 | * referred to using standard '?' JDBC substitutions 172 | * 173 | * @param statement SQL statement to execute. 174 | * @return PLResultSet on success, or nil on failure. 175 | */ 176 | - (id) executeQuery: (NSString *) statement, ...; 177 | 178 | /** 179 | * Execute a query, returning a PLResultSet. 180 | * 181 | * Any arguments should be provided following the statement, and 182 | * referred to using standard '?' JDBC substitutions 183 | * 184 | * @param error A pointer to an NSError object variable. If an error occurs, this 185 | * pointer will contain an error object indicating why the statement could not be executed. 186 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 187 | * parameter, and no error information will be provided. 188 | * @param statement SQL statement to execute. 189 | * @return PLResultSet on success, or nil on failure. 190 | */ 191 | - (id) executeQueryAndReturnError: (NSError **) error statement: (NSString *) statement, ...; 192 | 193 | /** 194 | * Begin a transaction and execute @a block. If @a block returns PLDatabaseTransactionRollback, and 195 | * the immediate proceeding database operation within the transaction block failed due to the server reporting a dead-lock 196 | * condition, the transaction will be rolled back, immediately retried, and @a block will executed again. 197 | * 198 | * @param block A block to be executed within the transaction. If the block returns PLDatabaseTransactionCommit, the 199 | * transaction will be committed, otherwise, the transaction will be rolled back and optionally retried. 200 | * @param outError If an error occurs executing the transaction, upon return contains an error object in the PLDatabaseErrorDomain 201 | * that describes the problem. Pass NULL if you do not want error information. 202 | * 203 | * @return YES if the transaction is successfully committed or rolled back, or NO on failure. Note that a return value of 204 | * YES does not signify that the transaction was committed, but rather, that no database error occured either committing 205 | * or rolling back the transaction. 206 | * 207 | * @par Automatic Retry 208 | * 209 | * If the immediate proceeding operation within the transaction failed due to a dead-lock condition, the transaction 210 | * will be automatically retried if PLDatabaseTransactionRollback is returned. This means that @a block may 211 | * be executed multiple times, and the block implementation must be idempotent and free of unintended side-effects if 212 | * run repeatedly. 213 | * 214 | * @par Isolation Level 215 | * 216 | * The transaction must provide at least 'Read committed' isolation. As per the SQL standard, the isolation level may be 217 | * stricter than what has been requested -- this method only gaurantees the MINIMUM of isolation. 218 | * 219 | * For more information on SQL standard transaction isolation levels, refer to 220 | * PostgreSQL's documentation: 221 | * http://www.postgresql.org/docs/8.3/interactive/transaction-iso.html 222 | * 223 | * @warning The provided @a block may be executed multiple times and must be idempotent. 224 | */ 225 | - (BOOL) performTransactionWithRetryBlock: (PLDatabaseTransactionResult (^)(void)) block error: (NSError **) outError; 226 | 227 | /** 228 | * Begin a transaction and execute @a block. If @a block returns PLDatabaseTransactionRollback, and 229 | * the immediate proceeding database operation within the transaction block failed due to the server reporting a dead-lock 230 | * condition, the transaction will be rolled back, immediately retried, and @a block will executed again. 231 | * 232 | * @param isolationLevel The minimum isolation level to be used for this transaction. 233 | * @param block A block to be executed within the transaction. If the block returns YES, the transaction will be committed, 234 | * otherwise, the transaction will be rolled back. 235 | * @param outError If an error occurs executing the transaction, upon return contains an error object in the PLDatabaseErrorDomain 236 | * that describes the problem. Pass NULL if you do not want error information. 237 | 238 | * @return YES if the transaction is successfully committed or rolled back, or NO on failure. Note that a return value of 239 | * YES does not signify that the transaction was committed, but rather, that no database error occured either committing 240 | * or rolling back the transaction. 241 | * 242 | * @par Automatic Retry 243 | * 244 | * If the immediate proceeding operation within the transaction failed due to a dead-lock condition, the transaction 245 | * will be automatically retried if PLDatabaseTransactionRollback is returned. This means that @a block may 246 | * be executed multiple times, and the block implementation must be idempotent and free of unintended side-effects if 247 | * run repeatedly. 248 | * 249 | * @warning The provided @a block may be executed multiple times and must be idempotent. 250 | */ 251 | - (BOOL) performTransactionWithIsolationLevel: (PLDatabaseIsolationLevel) isolationLevel 252 | retryBlock: (PLDatabaseTransactionResult (^)(void)) block 253 | error: (NSError **) outError; 254 | 255 | 256 | /** 257 | * Begin a transaction. This must provide at least 'Read committed' isolation. As 258 | * per the SQL standard, the isolation level may be stricter than what has been 259 | * requested -- this method only gaurantees the MINIMUM of isolation. 260 | * 261 | * For more information on SQL standard transaction isolation levels, refer to 262 | * PostgreSQL's documentation: 263 | * http://www.postgresql.org/docs/8.3/interactive/transaction-iso.html 264 | * 265 | * @return YES on success, NO on failure. 266 | */ 267 | - (BOOL) beginTransaction; 268 | 269 | /** 270 | * Begin a transaction. This must provide at least 'Read committed' isolation. As 271 | * per the SQL standard, the isolation level may be stricter than what has been 272 | * requested -- this method only gaurantees the MINIMUM of isolation. 273 | * 274 | * For more information on SQL standard transaction isolation levels, refer to 275 | * PostgreSQL's documentation: 276 | * http://www.postgresql.org/docs/8.3/interactive/transaction-iso.html 277 | * 278 | * @param outError A pointer to an NSError object variable. If an error occurs, this 279 | * pointer will contain an error object indicating why the transaction could not 280 | * be started. 281 | * 282 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 283 | * parameter, and no error information will be provided. 284 | * @return YES on success, NO on failure. 285 | */ 286 | - (BOOL) beginTransactionAndReturnError: (NSError **) outError; 287 | 288 | /** 289 | * Begin a transaction. 290 | * 291 | * @param isolationLevel The minimum isolation level to be used for this transaction. 292 | * @param outError A pointer to an NSError object variable. If an error occurs, this 293 | * pointer will contain an error object indicating why the transaction could not 294 | * be started. 295 | * 296 | * @return YES on success, NO on failure. 297 | */ 298 | - (BOOL) beginTransactionWithIsolationLevel: (PLDatabaseIsolationLevel) isolationLevel error: (NSError **) outError; 299 | 300 | /** 301 | * Commit an open transaction. 302 | * 303 | * @return YES on success, NO on failure. 304 | */ 305 | - (BOOL) commitTransaction; 306 | 307 | /** 308 | * Commit an open transaction. 309 | * 310 | * @param error A pointer to an NSError object variable. If an error occurs, this 311 | * pointer will contain an error object indicating why the transaction could not 312 | * be committed. 313 | * 314 | * @return YES on success, NO on failure. 315 | */ 316 | - (BOOL) commitTransactionAndReturnError: (NSError **) error; 317 | 318 | /** 319 | * Rollback an open transaction. 320 | * 321 | * @return YES on success, NO on failure. 322 | */ 323 | - (BOOL) rollbackTransaction; 324 | 325 | /** 326 | * Rollback an open transaction. 327 | * 328 | * @param error A pointer to an NSError object variable. If an error occurs, this 329 | * pointer will contain an error object indicating why the transaction could not 330 | * be rolled back. 331 | * 332 | * @return YES on success, NO on failure. 333 | */ 334 | - (BOOL) rollbackTransactionAndReturnError: (NSError **) error; 335 | 336 | 337 | /** 338 | * Return the number of rows modified by the last UPDATE, INSERT, or DELETE statement issued 339 | * on this connection. 340 | */ 341 | - (NSInteger) lastModifiedRowCount; 342 | 343 | /** 344 | * Return YES if the given table name exists. 345 | * 346 | * @return YES if it exists, NO otherwise. 347 | */ 348 | - (BOOL) tableExists: (NSString *) tableName; 349 | 350 | @end -------------------------------------------------------------------------------- /Classes/PLDatabaseConnectionProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | 33 | #import "PLDatabase.h" 34 | 35 | /** 36 | * A protocol for opening new or existing PLDatabase connections, and returning those connections for 37 | * re-use upon completion. 38 | * 39 | * @par Thread Safety 40 | * Thread-safe. May be used from any thread. 41 | * 42 | * @par Implementation Notes 43 | * 44 | * Implementations must be immutable and/or thread-safe, and must be usable from any thread without external 45 | * locking. 46 | */ 47 | @protocol PLDatabaseConnectionProvider 48 | 49 | /** 50 | * Returns a database connection. 51 | * 52 | * @param error A pointer to an NSError object variable. If an error occurs, this 53 | * pointer will contain an error object indicating why the transaction could not 54 | * be started. 55 | * 56 | * @return A database connection, or nil on error. 57 | */ 58 | - (id) getConnectionAndReturnError: (NSError **) error; 59 | 60 | /** 61 | * Called to return the given connection for re-use. The connection may be immediately closed. 62 | */ 63 | - (void) closeConnection: (id) connection; 64 | 65 | @end -------------------------------------------------------------------------------- /Classes/PLDatabaseConstants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | /* Exceptions */ 33 | extern NSString *PLDatabaseException; 34 | 35 | /* Error Domain and Codes */ 36 | extern NSString *PLDatabaseErrorDomain; 37 | extern NSString *PLDatabaseErrorQueryStringKey; 38 | extern NSString *PLDatabaseErrorVendorErrorKey; 39 | extern NSString *PLDatabaseErrorVendorStringKey; 40 | 41 | /** 42 | * NSError codes in the Plausible Database error domain. 43 | * @ingroup enums 44 | */ 45 | typedef enum { 46 | /** An unknown error has occured. If this 47 | * code is received, it is a bug, and should be reported. */ 48 | PLDatabaseErrorUnknown = 0, 49 | 50 | /** File not found. */ 51 | PLDatabaseErrorFileNotFound = 1, 52 | 53 | /** An SQL query failed. */ 54 | PLDatabaseErrorQueryFailed = 2, 55 | 56 | /** The provided SQL statement was invalid. */ 57 | PLDatabaseErrorInvalidStatement = 3, 58 | } PLDatabaseError; 59 | 60 | #ifdef PL_DB_PRIVATE 61 | 62 | @interface PlausibleDatabase : NSObject { 63 | } 64 | 65 | + (NSError *) errorWithCode: (PLDatabaseError) errorCode localizedDescription: (NSString *) localizedDescription 66 | queryString: (NSString *) queryString 67 | vendorError: (NSNumber *) vendorError vendorErrorString: (NSString *) vendorErrorString; 68 | 69 | @end 70 | 71 | #endif /* PL_DB_PRIVATE */ 72 | -------------------------------------------------------------------------------- /Classes/PLDatabaseFilterConnectionProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "PLDatabaseConnectionProvider.h" 32 | 33 | @interface PLDatabaseFilterConnectionProvider : NSObject { 34 | @private 35 | /** The backing connection provider. */ 36 | id _provider; 37 | 38 | /** The internal filter block */ 39 | id (^_filterBlock)(id); 40 | } 41 | 42 | - (id) initWithConnectionProvider: (id) provider filterBlock: (void (^)(id db)) filterBlock; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Classes/PLDatabaseFilterConnectionProvider.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLDatabaseFilterConnectionProvider.h" 31 | 32 | /** 33 | * Provides a filtering database connection provider that supports modification of PLDatabase connections as they are 34 | * returned from a backing PLDatabaseConnectionProvider. 35 | * 36 | * @par Thread Safety 37 | * Thread-safe. May be used from any thread. 38 | */ 39 | @implementation PLDatabaseFilterConnectionProvider 40 | 41 | 42 | /** 43 | * Initialize a new instance with the provided connection provider and filter block. 44 | * 45 | * @param provider A connection provider that will be used to acquire new database connections. 46 | * @param filterBlock The filter block to be called for each returned database connection. 47 | */ 48 | - (id) initWithConnectionProvider: (id) provider filterBlock: (void (^)(id db)) filterBlock { 49 | if ((self = [super init]) == nil) 50 | return nil; 51 | 52 | _provider = [provider retain]; 53 | _filterBlock = [filterBlock copy]; 54 | 55 | return self; 56 | } 57 | 58 | - (void) dealloc { 59 | [_provider release]; 60 | [_filterBlock release]; 61 | 62 | [super dealloc]; 63 | } 64 | 65 | // from PLDatabaseConnectionProvider protocol 66 | - (id) getConnectionAndReturnError: (NSError **) outError { 67 | /* Attempt to fetch the connection */ 68 | id db = [_provider getConnectionAndReturnError: outError]; 69 | if (db == nil) 70 | return nil; 71 | 72 | /* Apply a filter block */ 73 | _filterBlock(db); 74 | 75 | /* Return the filtered connection */ 76 | return db; 77 | } 78 | 79 | 80 | // from PLDatabaseConnectionProvider protocol 81 | - (void) closeConnection: (id) connection { 82 | [_provider closeConnection: connection]; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Classes/PLDatabaseFilterConnectionProviderTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqliteConnectionProvider.h" 33 | #import "PLDatabaseFilterConnectionProvider.h" 34 | 35 | @interface PLDatabaseFilterConnectionProviderTests : SenTestCase { 36 | @private 37 | } 38 | 39 | @end 40 | 41 | /** 42 | * PLFilterConnectionProvider Tests 43 | */ 44 | @implementation PLDatabaseFilterConnectionProviderTests 45 | 46 | /** 47 | * Test basic filtering. 48 | */ 49 | - (void) testFiltering { 50 | NSError *error; 51 | 52 | /* Create a testing database provider */ 53 | PLSqliteConnectionProvider *provider = [[[PLSqliteConnectionProvider alloc] initWithPath: @":memory:"] autorelease]; 54 | PLDatabaseFilterConnectionProvider *filter = [[[PLDatabaseFilterConnectionProvider alloc] initWithConnectionProvider: provider filterBlock: ^(id db) { 55 | NSError *error; 56 | 57 | STAssertNotNil(db, @"Filtering a nil database"); 58 | STAssertTrue([db executeUpdateAndReturnError: &error statement: @"PRAGMA user_version = 42;"], @"Failed to set user version: %@", error); 59 | }] autorelease]; 60 | 61 | /* Fetch a connection */ 62 | id con = [filter getConnectionAndReturnError: &error]; 63 | STAssertNotNil(con, @"Failed to fetch connection: %@", error); 64 | 65 | /* Verify that our filter block was applied. */ 66 | id rs = [con executeQueryAndReturnError: &error statement: @"PRAGMA user_version"]; 67 | STAssertNotNil(rs, @"Failed to execute pragma: %@", error); 68 | [rs next]; 69 | 70 | STAssertEquals(42, [rs intForColumn: @"user_version"], @"Incorrect version, filter not applied."); 71 | [rs close]; 72 | 73 | /* Check connection back in. */ 74 | [filter closeConnection: con]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationConnectionProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLDatabaseMigrationManager.h" 33 | #import "PLDatabaseConnectionProvider.h" 34 | 35 | @interface PLDatabaseMigrationConnectionProvider : NSObject { 36 | @private 37 | /** Backing connection provider. */ 38 | id _conProv; 39 | 40 | /** Migration manager. */ 41 | PLDatabaseMigrationManager *_migrationMgr; 42 | } 43 | 44 | - (id) initWithConnectionProvider: (id) conProv 45 | migrationManager: (PLDatabaseMigrationManager *) migrationManager; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationConnectionProvider.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLDatabaseMigrationConnectionProvider.h" 31 | 32 | 33 | /** 34 | * 35 | * The PLDatabaseMigrationConnectionProvider implements the PLDatabaseConnectionProvider protocol, providing 36 | * transactional migration of all returned connections via the PLDatabaseMigrationManager API. 37 | * 38 | * Connections are acquired (and returned) to a backing PLDatabaseConnectionProvider, and providers may be arbitrarily 39 | * stacked; for instance, a PLDatabaseMigrationConnectionProvider may be wrapped by a PLDatabaseConnectionPool, thus pooling 40 | * migrated connections. 41 | * 42 | * @par Thread Safety 43 | * Thread-safe. May be used from any thread. 44 | */ 45 | @implementation PLDatabaseMigrationConnectionProvider 46 | 47 | /** 48 | * Initialize a new migration connection provider. 49 | * 50 | * @param conProv The connection provider to be used to acquire and perform migrations upon new connections. 51 | * @param migrationManager The migration manager to be used to perform migrations on connections acquired from @a conProv. 52 | */ 53 | - (id) initWithConnectionProvider: (id) conProv 54 | migrationManager: (PLDatabaseMigrationManager *) migrationManager 55 | { 56 | if ((self = [super init]) == nil) 57 | return nil; 58 | 59 | _conProv = [conProv retain]; 60 | _migrationMgr = [migrationManager retain]; 61 | 62 | return self; 63 | } 64 | 65 | - (void) dealloc { 66 | [_conProv release]; 67 | [_migrationMgr release]; 68 | 69 | [super dealloc]; 70 | } 71 | 72 | // from PLDatabaseConnectionProvider protocol 73 | - (id) getConnectionAndReturnError: (NSError **) outError { 74 | /* Get the database connection */ 75 | id db = [_conProv getConnectionAndReturnError: outError]; 76 | if (db == nil) 77 | return nil; 78 | 79 | /* Run migrations */ 80 | if (![_migrationMgr migrateDatabase: db error: outError]) 81 | return nil; 82 | 83 | /* Success! */ 84 | return db; 85 | } 86 | 87 | // from PLDatabaseConnectionProvider protocol 88 | - (void) closeConnection: (id) connection { 89 | /* Simply hand the connection back to the backing provider. */ 90 | [_conProv closeConnection: connection]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationConnectionProviderTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLDatabaseMigrationConnectionProvider.h" 33 | #import "PLSqliteConnectionProvider.h" 34 | #import "PLSqliteMigrationManager.h" 35 | 36 | #define TEST_VERSION 42 37 | 38 | @interface PLDatabaseMigrationConnectionProviderTests : SenTestCase { 39 | @private 40 | } 41 | 42 | @end 43 | 44 | 45 | /** 46 | * PLDatabaseMigrationConnectionProvider Tests 47 | */ 48 | @implementation PLDatabaseMigrationConnectionProviderTests 49 | 50 | /** 51 | * Test simple migration 52 | */ 53 | - (void) testMigrate { 54 | PLDatabaseMigrationConnectionProvider *mprov; 55 | PLSqliteMigrationManager *sqliteMgr; 56 | PLSqliteConnectionProvider *prov; 57 | PLDatabaseMigrationManager *mgr; 58 | 59 | /* Set up the migration manager */ 60 | sqliteMgr = [[[PLSqliteMigrationManager alloc] init] autorelease]; 61 | mgr = [[[PLDatabaseMigrationManager alloc] initWithTransactionManager: sqliteMgr 62 | versionManager: sqliteMgr 63 | delegate: self] autorelease]; 64 | 65 | /* Set up the connection providers */ 66 | prov = [[[PLSqliteConnectionProvider alloc] initWithPath: @""] autorelease]; 67 | mprov = [[[PLDatabaseMigrationConnectionProvider alloc] initWithConnectionProvider: prov 68 | migrationManager: mgr] autorelease]; 69 | 70 | /* Try fetching a connection, and verify that it has been migrated. */ 71 | NSError *error; 72 | id db = [mprov getConnectionAndReturnError: &error]; 73 | STAssertNotNil(db, @"Failed to fetch and migrate a connection: %@", error); 74 | 75 | int version = 0; 76 | STAssertTrue([sqliteMgr version: &version forDatabase: db error: &error], @"Failed to fetch the database version: %@", error); 77 | STAssertEquals(TEST_VERSION, version, @"Database migration was not executed"); 78 | 79 | [mprov closeConnection: db]; 80 | } 81 | 82 | // from PLDatabaseMigrationDelegate 83 | - (BOOL) migrateDatabase: (id) database currentVersion: (int) currentVersion newVersion: (int *) newVersion error: (NSError **) outError { 84 | *newVersion = TEST_VERSION; 85 | return YES; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "PLDatabase.h" 32 | 33 | /** 34 | * The PLDatabaseMigrationDelegate is responsible for applying any migration necessary to update 35 | * a database to the latest schema/data required by an application. 36 | * 37 | * @par Thread Safety 38 | * Thread-safe. May be used from any thread. 39 | * 40 | * @par Implementation Notes 41 | * Implementations must be immutable and/or thread-safe, and must be usable from any thread without external 42 | * locking. 43 | */ 44 | @protocol PLDatabaseMigrationDelegate 45 | 46 | /** 47 | * Called by the PLDatabaseMigrationManager to perform migrations. 48 | * 49 | * A transaction will be opened prior to this method being called. The transaction will 50 | * be committed upon the return of a success value (YES). If this method returns NO, 51 | * the entire transaction will be aborted, and no changes made to the database. 52 | * 53 | * @param database The database to modify. 54 | * @param currentVersion The current version of the database. This always defaults to 0 in an uninitialized database. 55 | * @param newVersion Must be used to supply the new version of the database (or the current version, if nothing has changed). 56 | * @param outError A pointer to an NSError object variable. If an error occurs, this pointer will contain an error object indicating 57 | * why the migration could not be completed. If no error occurs, this parameter will be left unmodified. You may specify NULL for this 58 | * parameter, and no error information will be provided. 59 | */ 60 | - (BOOL) migrateDatabase: (id) database currentVersion: (int) currentVersion newVersion: (int *) newVersion error: (NSError **) outError; 61 | 62 | @end -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | 33 | #import "PLDatabaseConnectionProvider.h" 34 | 35 | #import "PLDatabaseMigrationTransactionManager.h" 36 | #import "PLDatabaseMigrationVersionManager.h" 37 | #import "PLDatabaseMigrationDelegate.h" 38 | 39 | @protocol PLDatabaseMigrationDelegate; 40 | 41 | @interface PLDatabaseMigrationManager : NSObject { 42 | @private 43 | /** The connection provider. */ 44 | id _connectionProvider; 45 | 46 | /** Lock manager used to start/commit transactions */ 47 | id _txManager; 48 | 49 | /** The version manager used to read/set the database version. */ 50 | id _versionManager; 51 | 52 | /** The delegate used to perform migrations. */ 53 | id _delegate; 54 | } 55 | 56 | - (id) initWithTransactionManager: (id) lockManager 57 | versionManager: (id) versionManager 58 | delegate: (id) delegate; 59 | 60 | - (BOOL) migrateDatabase: (id) database error: (NSError **) outError; 61 | 62 | - (id) initWithConnectionProvider: (id) connectionProvider 63 | transactionManager: (id) lockManager 64 | versionManager: (id) versionManager 65 | delegate: (id) delegate DEPRECATED_ATTRIBUTE; 66 | 67 | - (BOOL) migrateAndReturnError: (NSError **) outError DEPRECATED_ATTRIBUTE; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLDatabaseMigrationManager.h" 31 | 32 | /** 33 | * 34 | * The PLDatabaseMigrationManager implements transactional, versioned migration/initialization of 35 | * database schema and data. 36 | * 37 | * Arbitrary migrations may be supplied via a PLDatabaseMigrationDelegate. The database versioning 38 | * meta-data is maintained by a supplied PLDatabaseMigrationVersionManager. This class makes no 39 | * assumptions about the migrations applied or the methods used to retrieve or update 40 | * database schema versions. 41 | * 42 | * 43 | * @par Thread Safety 44 | * Thread-safe. May be used from any thread. 45 | */ 46 | @implementation PLDatabaseMigrationManager 47 | 48 | /** 49 | * Initialize a new migration manager. 50 | * 51 | * @param lockManager An object implementing the PLDatabaseMigrationTransactionManager protocol. 52 | * @param versionManager An object implementing the PLDatabaseMigrationVersionManager protocol. 53 | * @param delegate An object implementing the formal PLDatabaseMigrationDelegate protocol. 54 | */ 55 | - (id) initWithTransactionManager: (id) lockManager 56 | versionManager: (id) versionManager 57 | delegate: (id) delegate; 58 | { 59 | assert(delegate != nil); 60 | assert(lockManager != nil); 61 | assert(versionManager != nil); 62 | 63 | if ((self = [super init]) == nil) 64 | return nil; 65 | 66 | /* Save the delegates/providers */ 67 | _delegate = delegate; // cyclic reference, can not retain 68 | _txManager = [lockManager retain]; 69 | _versionManager = [versionManager retain]; 70 | 71 | return self; 72 | } 73 | 74 | /** 75 | * @deprecated Replaced by PLDatabaseMigrationManager::initWithTransactionManager:versionManager:delegate: 76 | */ 77 | - (id) initWithConnectionProvider: (id) connectionProvider 78 | transactionManager: (id) lockManager 79 | versionManager: (id) versionManager 80 | delegate: (id) delegate; 81 | { 82 | if ((self = [self initWithTransactionManager: lockManager versionManager: versionManager delegate: delegate]) == nil) 83 | return nil; 84 | 85 | /* Save the connection provider */ 86 | _connectionProvider = [connectionProvider retain]; 87 | 88 | return self; 89 | } 90 | 91 | 92 | - (void) dealloc { 93 | [_connectionProvider release]; 94 | [_txManager release]; 95 | [_versionManager release]; 96 | 97 | [super dealloc]; 98 | } 99 | 100 | /** 101 | * Perform any pending migrations on @a database using the receiver's PLDatabaseMigrationDelegate. 102 | * 103 | * @param db The database connection upon which migrations will be performed. 104 | * @param outError A pointer to an NSError object variable. If an error occurs, this 105 | * pointer will contain an error object indicating why the migration could not be completed. 106 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 107 | * parameter, and no error information will be provided. 108 | * @return YES on successful migration, or NO if migration failed. If NO is returned, all modifications 109 | * will be rolled back. 110 | */ 111 | - (BOOL) migrateDatabase: (id) db error: (NSError **) outError { 112 | int currentVersion; 113 | int newVersion; 114 | 115 | /* Start a transaction, we'll do *all modifications* within this one transaction */ 116 | if (![_txManager beginExclusiveTransactionForDatabase: db error: outError]) 117 | return NO; 118 | 119 | /* Fetch the current version. We default the new version to the current version -- failure to do so 120 | * will result in the database version being reset should the delegate forget to set the version 121 | * on a migration returning YES but implementing no changes. */ 122 | if (![_versionManager version: ¤tVersion forDatabase: db error: outError]) 123 | goto rollback; 124 | 125 | newVersion = currentVersion; 126 | 127 | /* Run the migration */ 128 | if (![_delegate migrateDatabase: db currentVersion: currentVersion newVersion: &newVersion error: outError]) 129 | goto rollback; 130 | 131 | if (![_versionManager setVersion: newVersion forDatabase: db error: outError]) 132 | goto rollback; 133 | 134 | if (![_txManager commitTransactionForDatabase: db error: outError]) 135 | goto rollback; 136 | 137 | /* Succeeded */ 138 | return YES; 139 | 140 | rollback: 141 | [_txManager rollbackTransactionForDatabase: db error: NULL]; 142 | return NO; 143 | } 144 | 145 | /** 146 | * @deprecated Replaced by PLDatabaseMigrationManager::migrateDatabase:error: 147 | */ 148 | - (BOOL) migrateAndReturnError: (NSError **) outError { 149 | id db; 150 | 151 | /* Open the database connection */ 152 | db = [_connectionProvider getConnectionAndReturnError: outError]; 153 | if (db == nil) { 154 | return NO; 155 | } 156 | 157 | /* Attempt the migration */ 158 | BOOL result = [self migrateDatabase: db error: outError]; 159 | 160 | /* Return our connection to the provider */ 161 | [_connectionProvider closeConnection: db]; 162 | 163 | return result; 164 | } 165 | 166 | @end 167 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationManagerTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqliteDatabase.h" 33 | #import "PLSqliteMigrationManager.h" 34 | #import "PLDatabaseMigrationManager.h" 35 | 36 | #define TEST_DATABASE_VERSION 42 37 | 38 | @interface PLDatabaseMigrationManagerTests : SenTestCase { 39 | @private 40 | NSString *_testDir; 41 | PLDatabaseMigrationManager *_dbManager; 42 | PLSqliteMigrationManager *_versionManager; 43 | } 44 | 45 | @end 46 | 47 | @interface PLDatabaseMigrationManagerTestsDelegateMock : NSObject { 48 | @private 49 | int _newVersion; 50 | BOOL _shouldFail; 51 | } 52 | @end 53 | 54 | @implementation PLDatabaseMigrationManagerTestsDelegateMock 55 | 56 | - (id) initWithNewVersion: (int) newVersion shouldFail: (BOOL) shouldFail { 57 | if((self = [super init]) == nil) 58 | return nil; 59 | 60 | _newVersion = newVersion; 61 | _shouldFail = shouldFail; 62 | 63 | return self; 64 | } 65 | 66 | - (BOOL) migrateDatabase: (id) database 67 | currentVersion: (int) currentVersion 68 | newVersion: (int *) newVersion 69 | error: (NSError **) outError 70 | { 71 | *newVersion = _newVersion; 72 | 73 | /* Create ourselves a table */ 74 | if (![database executeUpdateAndReturnError: outError statement: @"CREATE TABLE testtable (string VARCHAR(50))"]) 75 | return NO; 76 | 77 | if (_shouldFail) { 78 | if (outError != NULL) 79 | *outError = [NSError errorWithDomain: PLDatabaseErrorDomain code: PLDatabaseErrorUnknown userInfo: nil]; 80 | return NO; 81 | } else { 82 | return YES; 83 | } 84 | } 85 | 86 | @end 87 | 88 | 89 | @interface PLDatabaseMigrationManagerTestsDelegateDoNothingMock : NSObject @end 90 | 91 | /** 92 | * @internal 93 | * Migration delegate that does nothing, but returns success. Used to test whether the version 94 | * is set/reset/modified if the delegate fails to set the new version explicitly. 95 | */ 96 | @implementation PLDatabaseMigrationManagerTestsDelegateDoNothingMock 97 | 98 | - (BOOL) migrateDatabase: (id) database 99 | currentVersion: (int) currentVersion 100 | newVersion: (int *) newVersion 101 | error: (NSError **) outError 102 | { 103 | /* Do nothing */ 104 | return YES; 105 | } 106 | 107 | @end 108 | 109 | 110 | 111 | @implementation PLDatabaseMigrationManagerTests 112 | 113 | 114 | - (void) setUp { 115 | /* Create a temporary directory. Secure, as the user owns enclosing directory. */ 116 | _testDir = [[NSTemporaryDirectory() stringByAppendingPathComponent: [[NSProcessInfo processInfo] globallyUniqueString]] retain]; 117 | STAssertTrue([[NSFileManager defaultManager] createDirectoryAtPath: _testDir withIntermediateDirectories: YES attributes: nil error: NULL], 118 | @"Could not create test directory"); 119 | 120 | /* A new database manager */ 121 | PLDatabaseMigrationManagerTestsDelegateMock *delegate = [[[PLDatabaseMigrationManagerTestsDelegateMock alloc] initWithNewVersion: TEST_DATABASE_VERSION shouldFail: NO] autorelease]; 122 | 123 | _versionManager = [[PLSqliteMigrationManager alloc] init]; 124 | _dbManager = [[PLDatabaseMigrationManager alloc] initWithTransactionManager: _versionManager 125 | versionManager: _versionManager 126 | delegate: delegate]; 127 | STAssertNotNil(_dbManager, @"Could not create a new db manager"); 128 | } 129 | 130 | 131 | - (void) tearDown { 132 | BOOL result; 133 | 134 | /* Clean out the test directory */ 135 | result = [[NSFileManager defaultManager] removeItemAtPath: _testDir error: NULL]; 136 | STAssertTrue(result, @"Deletion of test directory returned error"); 137 | 138 | [_testDir release]; 139 | [_dbManager release]; 140 | [_versionManager release]; 141 | } 142 | 143 | 144 | - (void) testMigrate { 145 | NSError *error; 146 | int version; 147 | PLSqliteDatabase *database = [PLSqliteDatabase databaseWithPath: @":memory:"]; 148 | STAssertTrue([database openAndReturnError: &error], @"Could not get db connection: %@", error); 149 | 150 | /* Get the current version (should be 0) */ 151 | STAssertTrue([_versionManager version: &version forDatabase: database error: &error], @"Could not retrieve version: %@", error); 152 | STAssertTrue(0 == version, @"Expected database version 0, got %d", version); 153 | 154 | /* Run the migration */ 155 | STAssertTrue([_dbManager migrateDatabase: database error: &error], @"Migration failed: %@", error); 156 | 157 | /* Verify that our table was created and the version was bumped */ 158 | STAssertTrue([_versionManager version: &version forDatabase: database error: &error], @"Could not retrieve version: %@", error); 159 | STAssertTrue(TEST_DATABASE_VERSION == version, @"Expected database version %d, got %d", TEST_DATABASE_VERSION, version); 160 | STAssertTrue([database tableExists: @"testtable"], @"Test table was not created"); 161 | 162 | /* Clean up */ 163 | [database close]; 164 | } 165 | 166 | 167 | - (void) testMigrateRollback { 168 | PLDatabaseMigrationManagerTestsDelegateMock *delegate; 169 | PLDatabaseMigrationManager *dbManager; 170 | NSError *error; 171 | 172 | /* Set up our delegate */ 173 | delegate = [[[PLDatabaseMigrationManagerTestsDelegateMock alloc] initWithNewVersion: TEST_DATABASE_VERSION shouldFail: YES] autorelease]; 174 | dbManager = [[[PLDatabaseMigrationManager alloc] initWithTransactionManager: _versionManager 175 | versionManager: _versionManager 176 | delegate: delegate] autorelease]; 177 | 178 | /* Create a test database */ 179 | PLSqliteDatabase *database = [PLSqliteDatabase databaseWithPath: @":memory:"]; 180 | STAssertTrue([database openAndReturnError: &error], @"Could not get db connection: %@", error); 181 | 182 | /* Run the migration (will fail, should roll back) */ 183 | STAssertFalse([dbManager migrateDatabase: database error: NULL], @"Migration was expected to fail"); 184 | 185 | int version; 186 | STAssertTrue([_versionManager version: &version forDatabase: database error: &error], @"Could not retrieve database version: %@", error); 187 | STAssertEquals(0, version, @"The transaction was not rolled back, version is not 0"); 188 | STAssertFalse([database tableExists: @"testtable"], @"The transaction was not rolled back, table exists"); 189 | 190 | /* Clean up */ 191 | [database close]; 192 | } 193 | 194 | /** 195 | * Test handling of migrations that do not set the newVersion. 196 | */ 197 | - (void) testDoNothingMigration { 198 | PLDatabaseMigrationManagerTestsDelegateDoNothingMock *delegate; 199 | PLDatabaseMigrationManager *dbManager; 200 | NSError *error; 201 | 202 | /* Fetch a db connection */ 203 | PLSqliteDatabase *database = [PLSqliteDatabase databaseWithPath: @":memory:"]; 204 | STAssertTrue([database openAndReturnError: &error], @"Could not get db connection: %@", error); 205 | 206 | /* Start with a non-zero version */ 207 | assert(TEST_DATABASE_VERSION != 0); 208 | STAssertTrue([_versionManager setVersion: TEST_DATABASE_VERSION forDatabase: database error: &error], @"Could not set version: %@", error); 209 | 210 | /* Set up our delegate (will fail, should roll back) */ 211 | delegate = [[[PLDatabaseMigrationManagerTestsDelegateDoNothingMock alloc] init] autorelease]; 212 | dbManager = [[[PLDatabaseMigrationManager alloc] initWithTransactionManager: _versionManager 213 | versionManager: _versionManager 214 | delegate: delegate] autorelease]; 215 | 216 | /* Run the migration */ 217 | STAssertTrue([_dbManager migrateDatabase: database error: &error], @"Migration failed: %@", error); 218 | 219 | /* Verify that the version remains at TEST_DATABASE_VERSION */ 220 | int version; 221 | STAssertTrue([_versionManager version: &version forDatabase: database error: &error], @"Could not retrieve database version: %@", error); 222 | STAssertEquals(TEST_DATABASE_VERSION, version, @"The database version was reset"); 223 | 224 | /* Clean up */ 225 | [database close]; 226 | } 227 | 228 | @end 229 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationTransactionManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "PLDatabase.h" 32 | 33 | /** 34 | * Manages the database migration transactions and locking to ensure atomic testing 35 | * of the database version and application of any migrations. 36 | * 37 | * @par Thread Safety 38 | * Thread-safe. May be used from any thread. 39 | * 40 | * @par Implementation Notes 41 | * Implementations must be immutable and/or thread-safe, and must be usable from any thread without external 42 | * locking. 43 | */ 44 | @protocol PLDatabaseMigrationTransactionManager 45 | 46 | /** 47 | * Start a database transaction, using a sufficient isolation level and/or locking to ensure that 48 | * no other migrations will run until this has completed. 49 | * 50 | * @param database An active database connection on which to execute any queries. 51 | * @param outError A pointer to an NSError object variable. If an error occurs, this 52 | * pointer will contain an error object indicating why the transaction could not be started. 53 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 54 | * parameter, and no error information will be provided. 55 | */ 56 | - (BOOL) beginExclusiveTransactionForDatabase: (id) database error: (NSError **) outError; 57 | 58 | 59 | /** 60 | * Roll back the database transaction, returning YES on success, or NO on failure. 61 | * 62 | * @param database An active database connection on which to issue any queries. 63 | * @param outError A pointer to an NSError object variable. If an error occurs, this 64 | * pointer will contain an error object indicating why the transaction could not be 65 | * rolled back. 66 | */ 67 | - (BOOL) rollbackTransactionForDatabase: (id) database error: (NSError **) outError; 68 | 69 | 70 | /** 71 | * Commit the database transaction, returning YES on success, or NO on failure. 72 | * 73 | * @param database An active database connection on which to issue any queries. 74 | * @param outError A pointer to an NSError object variable. If an error occurs, this 75 | * pointer will contain an error object indicating why the transaction could not be 76 | * committed. 77 | */ 78 | - (BOOL) commitTransactionForDatabase: (id) database error: (NSError **) outError; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Classes/PLDatabaseMigrationVersionManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import "PLDatabase.h" 32 | 33 | /** 34 | * Manages the database migration version. The version may be stored using 35 | * any method. 36 | * 37 | * @par Thread Safety 38 | * Thread-safe. May be used from any thread. 39 | * 40 | * @par Implementation Notes 41 | * Implementations must be immutable and/or thread-safe, and must be usable from any thread without external 42 | * locking. 43 | */ 44 | @protocol PLDatabaseMigrationVersionManager 45 | 46 | /** 47 | * Retrieve the database's migration version, returning YES on success, or NO on failure. If the 48 | * database version is uninitialized, a value of 0 must be provided. This method may also 49 | * perform any necessary initialization of versioning meta-data tables. 50 | * 51 | * @param version A pointer to an NSInteger variable where the current migration version 52 | * will be stored on success. 53 | * @param database An active database connection on which to issue any queries. 54 | * @param outError A pointer to an NSError object variable. If an error occurs, this 55 | * pointer will contain an error object indicating why the version could not be retrieved. 56 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 57 | * parameter, and no error information will be provided. 58 | */ 59 | - (BOOL) version: (int *) version forDatabase: (id) database error: (NSError **) outError; 60 | 61 | /** 62 | * Set the database's migration version, returning YES on success, or NO on failure. 63 | * 64 | * A transaction will be opened prior to this method being called. The transaction will 65 | * be committed upon the return of a success value (YES). If this method returns NO, 66 | * the entire transaction will be aborted, and no changes made to the database. 67 | * 68 | * @param version The new migration version. 69 | * @param database An active database connection on which to issue any queries. 70 | * @param outError A pointer to an NSError object variable. If an error occurs, this 71 | * pointer will contain an error object indicating why the version could not be retrieved. 72 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 73 | * parameter, and no error information will be provided. 74 | */ 75 | - (BOOL) setVersion: (int) version forDatabase: (id) database error: (NSError **) outError; 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Classes/PLDatabasePoolConnectionProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import 32 | 33 | #import "PLDatabaseConnectionProvider.h" 34 | 35 | @interface PLDatabasePoolConnectionProvider : NSObject { 36 | @private 37 | /** Lock that must be held when mutating internal state. */ 38 | pthread_mutex_t _lock; 39 | 40 | /** The backing connection provider. */ 41 | id _provider; 42 | 43 | /** The set of available database connections. */ 44 | NSMutableSet *_connections; 45 | 46 | /** The maximum number of connections that may be cached by this pool. */ 47 | NSUInteger _capacity; 48 | } 49 | 50 | - (id) initWithConnectionProvider: (id) provider capacity: (NSUInteger) capacity; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Classes/PLDatabasePoolConnectionProvider.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLDatabasePoolConnectionProvider.h" 31 | 32 | /** 33 | * Provides a size-constrained thread-safe database connection pool. 34 | * 35 | * @par Thread Safety 36 | * Thread-safe. May be used from any thread, subject to SQLite's documented thread-safety constraints. 37 | */ 38 | @implementation PLDatabasePoolConnectionProvider 39 | 40 | /** 41 | * Initialize a new instance with the provided connection provider and capacity. 42 | * 43 | * @param provider A connection provider that will be used to acquire new database connections. 44 | * @param capacity The maximum number of database connections that the pool will cache. The pool will return 45 | * as many connections as are requested, but will not cache connections beyond this capacity. If a capacity of 0 is 46 | * specified, no capacity limit will be applied. 47 | */ 48 | - (id) initWithConnectionProvider: (id) provider capacity: (NSUInteger) capacity { 49 | if ((self = [super init]) == nil) 50 | return nil; 51 | 52 | _provider = [provider retain]; 53 | _capacity = capacity; 54 | 55 | if (capacity > 0) { 56 | _connections = [[NSMutableSet alloc] initWithCapacity: capacity]; 57 | } else { 58 | _connections = [[NSMutableSet alloc] init]; 59 | } 60 | 61 | pthread_mutex_init(&_lock, NULL); 62 | 63 | return self; 64 | } 65 | 66 | - (void) dealloc { 67 | [_provider release]; 68 | [_connections release]; 69 | 70 | pthread_mutex_destroy(&_lock); 71 | 72 | [super dealloc]; 73 | } 74 | 75 | // from PLDatabaseConnectionProvider protocol 76 | - (id) getConnectionAndReturnError: (NSError **) outError { 77 | id db; 78 | 79 | pthread_mutex_lock(&_lock); { 80 | /* Try to fetch an existing connection */ 81 | db = [[[_connections anyObject] retain] autorelease]; 82 | if (db != nil) 83 | [_connections removeObject: db]; 84 | } pthread_mutex_unlock(&_lock); 85 | 86 | 87 | /* No existing connection could be acquired; try to create a new connection. This may fail, and we just report the 88 | * error directly. We do this outside of the synchronized block to avoid any possibility of deadlock when calling 89 | * out to our backing provider. */ 90 | if (db == nil) { 91 | db = [_provider getConnectionAndReturnError: outError]; 92 | } 93 | 94 | return db; 95 | } 96 | 97 | 98 | // from PLDatabaseConnectionProvider protocol 99 | - (void) closeConnection: (id) connection { 100 | BOOL shouldClose = NO; 101 | 102 | pthread_mutex_lock(&_lock); { 103 | /* Check if we've hit capacity */ 104 | if (_capacity > 0 && [_connections count] >= _capacity) { 105 | shouldClose = YES; 106 | 107 | } else if (![connection goodConnection]) { 108 | /* Connection is invalid */ 109 | shouldClose = YES; 110 | 111 | } else { 112 | /* Connection is valid; re-add to the set of available connections. */ 113 | [_connections addObject: connection]; 114 | } 115 | } pthread_mutex_unlock(&_lock); 116 | 117 | /* We do this outside of the synchronized block to avoid any possibility of deadlock when calling 118 | * out to our backing provider. */ 119 | if (shouldClose) 120 | [_provider closeConnection: connection]; 121 | } 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /Classes/PLDatabasePoolConnectionProviderTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqliteConnectionProvider.h" 33 | #import "PLDatabasePoolConnectionProvider.h" 34 | 35 | @interface PLDatabasePoolConnectionProviderTests : SenTestCase { 36 | @private 37 | } 38 | 39 | @end 40 | 41 | @implementation PLDatabasePoolConnectionProviderTests 42 | 43 | /** 44 | * Test basic pooling. 45 | */ 46 | - (void) testPooling { 47 | NSError *error; 48 | 49 | /* Create a testing database pool */ 50 | PLSqliteConnectionProvider *provider = [[[PLSqliteConnectionProvider alloc] initWithPath: @":memory:"] autorelease]; 51 | PLDatabasePoolConnectionProvider *pool = [[[PLDatabasePoolConnectionProvider alloc] initWithConnectionProvider: provider capacity: 0] autorelease]; 52 | 53 | /* Fetch a connection */ 54 | id con = [pool getConnectionAndReturnError: &error]; 55 | STAssertNotNil(con, @"Failed to fetch connection: %@", error); 56 | 57 | /* Verify that another connection is returned if we request it before returning our existing connection to the pool. */ 58 | id cachedConnection = [pool getConnectionAndReturnError: &error]; 59 | STAssertNotNil(con, @"Failed to fetch connection: %@", error); 60 | STAssertTrue(con != cachedConnection, @"Returned an already checked out connection"); 61 | 62 | /* Check out connection back in, verify that it will be returned if we ask for a new connection. */ 63 | [pool closeConnection: con]; 64 | cachedConnection = [pool getConnectionAndReturnError: &error]; 65 | STAssertEquals(con, cachedConnection, @"Did not return expected connection"); 66 | } 67 | 68 | /** 69 | * Test capacity handling 70 | */ 71 | - (void) testCapacity { 72 | NSError *error; 73 | 74 | /* Create a testing database pool */ 75 | PLSqliteConnectionProvider *provider = [[[PLSqliteConnectionProvider alloc] initWithPath: @":memory:"] autorelease]; 76 | PLDatabasePoolConnectionProvider *pool = [[[PLDatabasePoolConnectionProvider alloc] initWithConnectionProvider: provider capacity: 1] autorelease]; 77 | 78 | /* Fetch two connections and check one back in; the cache should now be at capacity. */ 79 | id con1 = [pool getConnectionAndReturnError: &error]; 80 | id con2 = [pool getConnectionAndReturnError: &error]; 81 | 82 | STAssertNotNil(con1, @"Failed to fetch connection: %@", error); 83 | STAssertNotNil(con2, @"Failed to fetch connection: %@", error); 84 | 85 | [pool closeConnection: con2]; 86 | 87 | /* Verify that con2 was not closed; it should be cached. */ 88 | STAssertTrue([con2 goodConnection], @"Connection was closed, but cache isn't yet at capacity"); 89 | 90 | /* Check the other connection back in, and verify that it is closed by the cache. */ 91 | [pool closeConnection: con1]; 92 | STAssertFalse([con1 goodConnection], @"Cache is at capacity, but connection was not closed"); 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Classes/PLPreparedStatement.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | #import "PLDatabaseConstants.h" 33 | #import "PLResultSet.h" 34 | 35 | /** 36 | * An object that represents a pre-compiled statement, and any parameters 37 | * bound to that statement. 38 | * 39 | * @par SQL Parameters 40 | * Literal query values may be substituted via parameter binding, by using 41 | * the '?' symbol in the query statement and PLPreparedStatement::bindParameters. 42 | * 43 | * Additionally, PLPreparedStatement::bindParameterDictionary implements 44 | * name-based parameter binding. To bind named parameters, use the ':<name>' 45 | * syntax. All named parameters must be provided in the binding parameter 46 | * dictionary, and it is not possible to mix named and unnamed parameters 47 | * when using PLPreparedStatement::bindParameterDictionary. 48 | * 49 | * @par Thread Safety 50 | * PLPreparedStatement instances implement no locking and must not be shared between threads 51 | * without external synchronization. 52 | * 53 | * @warning A prepared statement may not be re-used by simultaneous PLResultSet. Attempting to 54 | * either re-execute a statement or rebind its parameters without first closing any PLResultSet previously 55 | * returned by the statement will throw an exception. 56 | */ 57 | @protocol PLPreparedStatement 58 | 59 | /** 60 | * Returns the number of parameters in the prepared statement. 61 | */ 62 | - (int) parameterCount; 63 | 64 | /** 65 | * Bind a list of parameters to the prepared statement. All parameters 66 | * must be provided -- if less than PLPreparedStatement::parameterCount 67 | * values are provided, an exception will be thrown. 68 | * 69 | * @param parameters List of parameters to bind. 70 | * @note NSArray may not contain nil values. Any nil parameter values must be 71 | * supplied using NSNull. 72 | */ 73 | - (void) bindParameters: (NSArray *) parameters; 74 | 75 | /** 76 | * If a statement was created using named parameters, the parameter 77 | * values may be bound using a dictionary mapping the parameter name 78 | * to its intended value. 79 | * 80 | * @param parameters Dictionary of named parameters to bind. 81 | * @note NSDictionary may not contain nil values. Any nil parameter values must be 82 | * supplied using NSNull. 83 | */ 84 | - (void) bindParameterDictionary: (NSDictionary *) parameters; 85 | 86 | /** 87 | * Execute an update, returning YES on success, NO on failure. 88 | */ 89 | - (BOOL) executeUpdate; 90 | 91 | 92 | /** 93 | * Execute an update, returning YES on success, NO on failure. 94 | * 95 | * @param outError A pointer to an NSError object variable. If an error occurs, this 96 | * pointer will contain an error object indicating why the statement could not be executed. 97 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 98 | * parameter, and no error information will be provided. 99 | */ 100 | - (BOOL) executeUpdateAndReturnError: (NSError **) outError; 101 | 102 | 103 | /** 104 | * Execute a query, returning a PLResultSet. 105 | * 106 | * @return PLResultSet on success, or nil on failure. 107 | */ 108 | - (id) executeQuery; 109 | 110 | 111 | /** 112 | * Execute a query, returning a PLResultSet. 113 | * 114 | * @param outError A pointer to an NSError object variable. If an error occurs, this 115 | * pointer will contain an error object indicating why the statement could not be executed. 116 | * If no error occurs, this parameter will be left unmodified. You may specify NULL for this 117 | * parameter, and no error information will be provided. 118 | * @return PLResultSet on success, or nil on failure. 119 | */ 120 | - (id) executeQueryAndReturnError: (NSError **) outError; 121 | 122 | /** 123 | * Close the prepared statement, and return any held database resources. After calling, 124 | * no further PLPreparedStatement methods may be called on the instance. 125 | * 126 | * As PLPreparedStatement objects may be placed into autorelease pools with indeterminate 127 | * release of database resources, this method may be used to ensure that resources 128 | * are free'd in a timely fashion. 129 | * 130 | * Failure to call close will not result in any memory leaks. 131 | */ 132 | - (void) close; 133 | 134 | @end -------------------------------------------------------------------------------- /Classes/PLResultSet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | #import "PLDatabaseConstants.h" 33 | 34 | /** 35 | * Result values returned when iterating PLResetSet rows. 36 | * 37 | * @ingroup constants 38 | */ 39 | typedef enum { 40 | /** No further rows available */ 41 | PLResultSetStatusDone = 0, 42 | 43 | /** An additional row is available. */ 44 | PLResultSetStatusRow = 1, 45 | 46 | /** An error occured retrieving the row. */ 47 | PLResultSetStatusError = 2 48 | } PLResultSetStatus; 49 | 50 | /** 51 | * Represents a set of results returned by an SQL query. 52 | * 53 | * @par Thread Safety 54 | * PLResultSet instances implement no locking and must not be shared between threads 55 | * without external synchronization. 56 | */ 57 | @protocol PLResultSet 58 | 59 | /** 60 | * Iterate over all rows in the result set, calling the provided block for each row. 61 | * 62 | * @param block Block to execute for each row in the result set. Set the provided stop argument's value 63 | * to YES to stop iteration of the result set. 64 | * 65 | * @return Returns YES if the result set was successfully iterated, or NO if a database error occurs. 66 | * 67 | * @invariant If all rows are enumerated and iteration is not explicitly stopped by setting the provided stop argument, the result set will be implicitly closed. 68 | * @invariant If an error occurs during enumeration and NO is returned by this method, the result set will be implicitly closed. 69 | */ 70 | - (BOOL) enumerateWithBlock: (void (^)(id rs, BOOL *stop)) block; 71 | 72 | /** 73 | * Iterate over all rows in the result set, calling the provided block for each row. 74 | * 75 | * @param outError A pointer to an NSError object variable. If an error occurs, iteration will stop and 76 | * this pointer will contain an error object indicating why the statement could not be executed. 77 | * If no error occurs, this parameter's value will not be modified. You may specify NULL for this 78 | * parameter, and no error information will be provided. 79 | * @param block Block to execute for each row in the result set. Set the provided stop argument's value 80 | * to YES to stop iteration of the result set. 81 | * 82 | * @return Returns YES if the result set was successfully iterated, or NO if a database error occurs. 83 | * 84 | * @invariant If all rows are enumerated and iteration is not explicitly stopped by setting the provided stop argument, the result set will be implicitly closed. 85 | * @invariant If an error occurs during enumeration and NO is returned by this method, the result set will be implicitly closed. 86 | */ 87 | - (BOOL) enumerateAndReturnError: (NSError **) outError block: (void (^)(id rs, BOOL *stop)) block; 88 | 89 | /** 90 | * Move the result cursor to the next available row. If no further rows 91 | * are available or an error occurs, returns NO. 92 | * 93 | * @return YES if the cursor was moved to the next row, NO if no further rows were available or an error 94 | * has occured. 95 | * 96 | * @deprecated This method fails to differentiate between end of rows and an error condition. Replaced by 97 | * PLResultSet::nextAndReturnError:. 98 | */ 99 | - (BOOL) next; 100 | 101 | /** 102 | * Move the result cursor to the next available row. If no further rows 103 | * are available or an error occurs, returns NO. 104 | * 105 | * @param outError A pointer to an NSError object variable. If an error occurs, this 106 | * pointer will contain an error object indicating why the statement could not be executed. 107 | * If no error occurs, this parameter's value will not be modified. You may specify NULL for this 108 | * parameter, and no error information will be provided. 109 | * 110 | * @return Returns #PLResultSetStatusRow if the next row is available, or #PLResultSetStatusDone if no 111 | * further rows are available. If an error occurs, #PLResultSetStatusError will be returned. 112 | */ 113 | - (PLResultSetStatus) nextAndReturnError: (NSError **) outError; 114 | 115 | /** 116 | * Close the result set, and return any held database resources. After calling, 117 | * no further PLResultSet methods may be called on the instance. 118 | * 119 | * As PLResultSet objects may be placed into autorelease pools, with indeterminate 120 | * release of database resources, this method should be used to ensure that the 121 | * database connection remains usable once finished with a result set. 122 | * 123 | * Failure to call close will not result in any memory leaks, but may prevent 124 | * further use of the database connection (until the result set is released). 125 | */ 126 | - (void) close; 127 | 128 | /** 129 | * Map the given column name to a column index. Will throw NSException if the column name is unknown. 130 | * 131 | * @param name Name of the column. 132 | * @return Returns the index of the column name, or throws an NSException if the column can not be found. 133 | */ 134 | - (int) columnIndexForName: (NSString *) name; 135 | 136 | /** 137 | * Return the integer value of the given column index from the current result row. 138 | * 139 | * If the column value is NULL, 0 will be returned. 140 | * 141 | * Will throw NSException if the column index is out of range. 142 | */ 143 | - (int32_t) intForColumnIndex: (int) columnIdx; 144 | 145 | /** 146 | * Return the integer value of the named column from the current result row. 147 | * 148 | * If the column value is NULL, 0 will be returned. 149 | * 150 | * Will throw NSException if the column name is unknown. 151 | */ 152 | - (int32_t) intForColumn: (NSString *) columnName; 153 | 154 | /** 155 | * Return the string value of the given column index from the current result row. 156 | * 157 | * If the column value is NULL, nil will be returned. 158 | * 159 | * Will throw NSException if the column index is out of range. 160 | */ 161 | - (NSString *) stringForColumnIndex: (int) columnIndex; 162 | 163 | /** 164 | * Return the string value of the named column from the current result row. 165 | * 166 | * If the column value is NULL, nil will be returned. 167 | * 168 | * Will throw NSException if the column name is unknown. 169 | */ 170 | - (NSString *) stringForColumn: (NSString *) columnName; 171 | 172 | /** 173 | * Returns the 64 bit big integer (long) value of the given column index the current result row. 174 | * 175 | * If the column value is NULL, 0 will be returned. 176 | * 177 | * Will throw NSException if the column index is out of range. 178 | */ 179 | - (int64_t) bigIntForColumnIndex: (int) columnIndex; 180 | 181 | /** 182 | * Returns the 64 bit big integer (long) value of the named column from the current result row. 183 | * 184 | * If the column value is NULL, 0 will be returned. 185 | * 186 | * Will throw NSException if the column name is unknown. 187 | */ 188 | - (int64_t) bigIntForColumn: (NSString *) columnName; 189 | 190 | /** 191 | * Returns YES if the value of the given column index is NULL, 192 | * NO otherwise. 193 | * 194 | * Will throw NSException if the column index is out of range. 195 | */ 196 | - (BOOL) isNullForColumnIndex: (int) columnIndex; 197 | 198 | /** 199 | * Returns YES if the value of the named column is NULL, 200 | * NO otherwise. 201 | * 202 | * Will throw NSException if the column index is out of range. 203 | */ 204 | - (BOOL) isNullForColumn: (NSString *) columnName; 205 | 206 | /** 207 | * Returns the BOOL value of the named column from the current result row. 208 | * 209 | * If the column value is NULL, NO will be returned. 210 | * 211 | * Will throw NSException if the column name is unknown. 212 | */ 213 | - (BOOL) boolForColumn: (NSString *) columnName; 214 | 215 | /** 216 | * Returns the BOOL value of the given column index from the current result row. 217 | * 218 | * If the column value is NULL, NO will be returned. 219 | * 220 | * Will throw NSException if the column index is out of range. 221 | */ 222 | - (BOOL) boolForColumnIndex: (int) columnIndex; 223 | 224 | /** 225 | * Returns the float value of the named column from the current result row. 226 | * 227 | * If the column value is NULL, 0.0 will be returned. 228 | * 229 | * Will throw NSException if the column name is unknown. 230 | */ 231 | - (float) floatForColumn: (NSString *) columnName; 232 | 233 | /** 234 | * Returns the float value of the given column index from the current result row. 235 | * 236 | * If the column value is NULL, 0.0 will be returned. 237 | * 238 | * Will throw NSException if the column index is out of range, 239 | * or if the column value is NULL. 240 | */ 241 | - (float) floatForColumnIndex: (int) columnIndex; 242 | 243 | /** 244 | * Returns the double value of the named column from the current result row. 245 | * 246 | * If the column value is NULL, 0.0 will be returned. 247 | * 248 | * Will throw NSException if the column name is unknown. 249 | */ 250 | - (double) doubleForColumn: (NSString *) columnName; 251 | 252 | /** 253 | * Returns the double value of the given column index from the current result row. 254 | * 255 | * If the column value is NULL, 0.0 will be returned. 256 | * 257 | * Will throw NSException if the column index is out of range. 258 | */ 259 | - (double) doubleForColumnIndex: (int) columnIndex; 260 | 261 | /** 262 | * Returns the NSDate value of the named column from the current result row. 263 | * 264 | * If the column value is NULL, nil will be returned. 265 | * 266 | * Will throw NSException if the column name is unknown. 267 | */ 268 | - (NSDate *) dateForColumn: (NSString *) columnName; 269 | 270 | /** 271 | * Returns the NSDate value of the given column index from the current result row. 272 | * 273 | * If the column value is NULL, nil will be returned. 274 | * 275 | * Will throw NSException if the column index is out of range. 276 | */ 277 | - (NSDate *) dateForColumnIndex: (int) columnIndex; 278 | 279 | /** 280 | * Returns the NSData value of the named column from the current result row. 281 | * 282 | * If the column value is NULL, nil will be returned. 283 | * 284 | * Will throw NSException if the column name is unknown. 285 | */ 286 | - (NSData *) dataForColumn: (NSString *) columnName; 287 | 288 | /** 289 | * Returns the NSData value of the given column index from the current result row. 290 | * 291 | * If the column value is NULL, nil will be returned. 292 | * 293 | * Will throw NSException if the column index is out of range. 294 | */ 295 | - (NSData *) dataForColumnIndex: (int) columnIndex; 296 | 297 | /** 298 | * Return the value of the named column as a Foundation Objective-C object, using the database driver's built-in 299 | * SQL and Foundation data-type mappings. 300 | * 301 | * If the column value is NULL, nil will be returned. 302 | * 303 | * Will throw NSException if the column name is unknown. 304 | * 305 | * @param columnName Name of column value to return. 306 | * 307 | * @warning In previous releases, NSNull was returned for NULL column values. This behavior was not documented, 308 | * and the implementation has been modified to return nil. 309 | */ 310 | - (id) objectForColumn: (NSString *) columnName; 311 | 312 | /** 313 | * Return the value of the named column as a Foundation Objective-C object, using the database driver's built-in 314 | * SQL and Foundation data-type mappings. 315 | * 316 | * If the column value is NULL, nil will be returned. 317 | * 318 | * Will throw NSException if the column index is out of range. 319 | * 320 | * @param columnIndex Index of column value to return. 321 | * 322 | * @warning In previous releases, NSNull was returned for NULL column values. This behavior was not documented, 323 | * and the implementation has been modified to return nil. 324 | */ 325 | - (id) objectForColumnIndex: (int) columnIndex; 326 | 327 | /** 328 | * Return the value of the named column as a Foundation Objective-C object, using the database driver's built-in 329 | * SQL and Foundation data-type mappings. 330 | * 331 | * If the column value is NULL, nil will be returned. 332 | * 333 | * Will throw NSException if the column name is unknown. 334 | * 335 | * @param columnName Name of column value to return. 336 | * 337 | * @note This method provides support for Objective-C's subscript syntax, and is otherwise identical to PLResultSet::objectForColumn:. 338 | */ 339 | - (id) objectForKeyedSubscript: (id)key; 340 | 341 | /** 342 | * Return the value of the named column as a Foundation Objective-C object, using the database driver's built-in 343 | * SQL and Foundation data-type mappings. 344 | * 345 | * If the column value is NULL, nil will be returned. 346 | * 347 | * Will throw NSException if the column index is out of range. 348 | * 349 | * @param columnIndex Index of column value to return. 350 | * 351 | * @note This method provides support for Objective-C's subscript syntax, and is otherwise identical to PLResultSet::objectForColumnIndex:. 352 | */ 353 | - (id) objectAtIndexedSubscript: (NSUInteger) index; 354 | 355 | 356 | 357 | @end 358 | 359 | -------------------------------------------------------------------------------- /Classes/PLSqliteConnectionProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | 33 | #import "PLDatabaseConnectionProvider.h" 34 | 35 | @interface PLSqliteConnectionProvider : NSObject { 36 | @private 37 | /** Path to backing database */ 38 | NSString *_dbPath; 39 | 40 | /** SQLite open flags. */ 41 | int _flags; 42 | 43 | /** If NO, ignore _flags and allow the database driver to set the default flags. */ 44 | BOOL _useFlags; 45 | } 46 | 47 | - (id) initWithPath: (NSString *) dbPath; 48 | - (id) initWithPath: (NSString *) dbPath flags: (int) flags; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Classes/PLSqliteConnectionProvider.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLSqliteConnectionProvider.h" 31 | #import "PLSqliteDatabase.h" 32 | 33 | /** 34 | * Provides new PLSqliteDatabase connections as per the PLDatabaseConnectionProvider 35 | * protocol. This class does no connection pooling, and should be combined 36 | * with a generic connection pool implementation if pooling is required. 37 | * 38 | * @par Thread Safety 39 | * Immutable and thread-safe. May be used from any thread. 40 | */ 41 | @implementation PLSqliteConnectionProvider 42 | 43 | /* 44 | * Private initializer. 45 | * 46 | * @param dbPath Path to the sqlite database file. 47 | * @param flags The SQLite-defined flags that will be passed directly to sqlite3_open_v2() or an equivalent API. 48 | * @return YES if the database was successfully opened, NO on failure. 49 | * @param useFlags If NO, the @a flags argument will be ignored. 50 | */ 51 | - (id) initWithPath: (NSString *) dbPath flags: (int) flags useFlags: (BOOL) useFlags { 52 | if ((self = [super init]) == nil) 53 | return nil; 54 | 55 | /* Path to our backing database */ 56 | _dbPath = [dbPath retain]; 57 | _flags = flags; 58 | _useFlags = useFlags; 59 | 60 | return self; 61 | } 62 | 63 | /** 64 | * Initialize the database connection delegate with the provided 65 | * file path. 66 | * 67 | * @param dbPath Path to the sqlite database file. 68 | */ 69 | - (id) initWithPath: (NSString *) dbPath { 70 | return [self initWithPath: dbPath flags: 0 useFlags: NO]; 71 | } 72 | 73 | /** 74 | * Initialize the database connection delegate with the provided 75 | * file path. 76 | * 77 | * @param dbPath Path to the sqlite database file. 78 | * @param flags The SQLite-defined flags that will be passed directly to sqlite3_open_v2() or an equivalent API. 79 | * @return YES if the database was successfully opened, NO on failure. 80 | * 81 | * @par Supported Flags 82 | * The flags supported by SQLite are defined in the SQLite C API Documentation: 83 | * http://www.sqlite.org/c3ref/open.html 84 | */ 85 | - (id) initWithPath: (NSString *) dbPath flags: (int) flags { 86 | return [self initWithPath: dbPath flags: flags useFlags: YES]; 87 | } 88 | 89 | 90 | 91 | - (void) dealloc { 92 | [_dbPath release]; 93 | 94 | [super dealloc]; 95 | } 96 | 97 | // from PLDatabaseConnectionProvider protocol 98 | - (id) getConnectionAndReturnError: (NSError **) error { 99 | PLSqliteDatabase *database; 100 | 101 | /* Create and attempt to open */ 102 | database = [[[PLSqliteDatabase alloc] initWithPath: _dbPath] autorelease]; 103 | 104 | /* Open with the correct flags (or the default flags) */ 105 | BOOL ret; 106 | if (_useFlags) { 107 | ret = [database openWithFlags: _flags error: error]; 108 | } else { 109 | ret = [database openAndReturnError: error]; 110 | } 111 | 112 | if (!ret) { 113 | /* Error was filled in, simply return nil */ 114 | return nil; 115 | } 116 | 117 | /* All is well, and database connection is open */ 118 | return database; 119 | } 120 | 121 | 122 | // from PLDatabaseConnectionProvider protocol 123 | - (void) closeConnection: (id) connection { 124 | // Nothing to do besides close the connection, no connection pooling 125 | [connection close]; 126 | } 127 | 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /Classes/PLSqliteConnectionProviderTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqliteDatabase.h" 33 | #import "PLSqliteConnectionProvider.h" 34 | 35 | @interface PLSqliteConnectionProviderTests : SenTestCase { 36 | @private 37 | NSString *_dbPath; 38 | } 39 | @end 40 | 41 | @implementation PLSqliteConnectionProviderTests 42 | 43 | - (void) setUp { 44 | /* Create a temporary file for the database. Secure -- user owns enclosing directory. */ 45 | _dbPath = [[NSTemporaryDirectory() stringByAppendingPathComponent: [[NSProcessInfo processInfo] globallyUniqueString]] retain]; 46 | } 47 | 48 | - (void) tearDown { 49 | /* Remove the temporary database file */ 50 | if ([[NSFileManager defaultManager] fileExistsAtPath: _dbPath]) 51 | STAssertTrue([[NSFileManager defaultManager] removeItemAtPath: _dbPath error: NULL], @"Could not clean up database %@", _dbPath); 52 | 53 | /* Release our objects */ 54 | [_dbPath release]; 55 | } 56 | 57 | - (void) testInitWithFlags { 58 | PLSqliteConnectionProvider *provider; 59 | id db; 60 | NSError *error; 61 | 62 | /* Create our delegate and request a connection, verifying that it is created */ 63 | provider = [[[PLSqliteConnectionProvider alloc] initWithPath: _dbPath flags: SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE] autorelease]; 64 | db = [provider getConnectionAndReturnError: &error]; 65 | 66 | /* Test the connection */ 67 | STAssertNotNil(db, @"Delegate returned nil: %@", error); 68 | STAssertTrue([db goodConnection], @"Database connection claims to be bad."); 69 | 70 | /* Try to be polite */ 71 | [provider closeConnection: db]; 72 | STAssertFalse([db goodConnection], @"Connection should be closed"); 73 | STAssertTrue([[NSFileManager defaultManager] removeItemAtPath: _dbPath error: NULL], @"Could not clean up database %@", _dbPath); 74 | 75 | /* Create our second provider -- the lack of SQLITE_OPEN_CREATE should ensure that opening the connection fails, 76 | * as the database does not exist. */ 77 | provider = [[[PLSqliteConnectionProvider alloc] initWithPath: _dbPath flags: SQLITE_OPEN_READWRITE] autorelease]; 78 | db = [provider getConnectionAndReturnError: NULL]; 79 | STAssertNil(db, @"Database open flag was not specified -- provider created a database even though SQLITE_OPEN_CREATE|SQLITE_OPEN_EXCLUSIVE was set"); 80 | } 81 | 82 | - (void) testInitWithPath { 83 | PLSqliteConnectionProvider *provider; 84 | id db; 85 | 86 | /* Create our delegate and request a connection */ 87 | provider = [[[PLSqliteConnectionProvider alloc] initWithPath: _dbPath] autorelease]; 88 | db = [provider getConnectionAndReturnError: NULL]; 89 | 90 | /* Test the connection */ 91 | STAssertNotNil(db, @"Delegate returned nil."); 92 | STAssertTrue([db goodConnection], @"Database connection claims to be bad."); 93 | 94 | /* Try to be polite */ 95 | [provider closeConnection: db]; 96 | STAssertFalse([db goodConnection], @"Connection should be closed"); 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Classes/PLSqliteDatabase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import 32 | 33 | /* On older versions of sqlite3, sqlite3_prepare_v2() is not available. It was introduced in 3.3.9, and significant 34 | * bugs remained until 3.3.11. */ 35 | #if SQLITE_VERSION_NUMBER < 3003011 36 | #error SQLite versions 3.3.8 and earlier are unsupported. 37 | #endif 38 | 39 | #import "PLDatabase.h" 40 | #import "PLSqliteStatementCache.h" 41 | 42 | extern NSString *PLSqliteException; 43 | 44 | @interface PLSqliteDatabase : NSObject { 45 | @private 46 | /** Path to the database file. */ 47 | NSString *_path; 48 | 49 | /** Underlying sqlite database reference. */ 50 | sqlite3 *_sqlite; 51 | 52 | /** If YES, a transaction monitored for SQLITE_BUSY is currently active. In the future, we may replace this with a 53 | * stack to automatically support nested transactions via save points. */ 54 | BOOL _monitorTx; 55 | 56 | /** If YES, SQLITE_BUSY was returned from a monitored transaction. */ 57 | BOOL _txBusy; 58 | 59 | /** Prepared statement cache */ 60 | PLSqliteStatementCache *_statementCache; 61 | } 62 | 63 | + (id) databaseWithPath: (NSString *) dbPath; 64 | 65 | - (id) initWithPath: (NSString*) dbPath; 66 | 67 | - (BOOL) open; 68 | - (BOOL) openAndReturnError: (NSError **) error; 69 | 70 | - (BOOL) openWithFlags: (int) flags; 71 | - (BOOL) openWithFlags: (int) flags error: (NSError **) error; 72 | 73 | - (sqlite3 *) sqliteHandle; 74 | - (int64_t) lastInsertRowId; 75 | 76 | @end 77 | 78 | #ifdef PL_DB_PRIVATE 79 | 80 | @interface PLSqliteDatabase (PLSqliteDatabaseLibraryPrivate) 81 | 82 | - (int) lastErrorCode; 83 | - (NSString *) lastErrorMessage; 84 | 85 | - (void) resetTxBusy; 86 | - (void) setTxBusy; 87 | 88 | #ifdef PL_SQLITE_LEGACY_STMT_PREPARE 89 | // This method is only exposed for the purpose of supporting implementations missing sqlite3_prepare_v2() 90 | - (sqlite3_stmt *) createStatement: (NSString *) statement error: (NSError **) error; 91 | #endif 92 | 93 | - (void) populateError: (NSError **) result withErrorCode: (PLDatabaseError) errorCode 94 | description: (NSString *) localizedDescription queryString: (NSString *) queryString; 95 | 96 | @end 97 | 98 | #endif -------------------------------------------------------------------------------- /Classes/PLSqliteMigrationManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLDatabaseMigrationManager.h" 33 | 34 | @interface PLSqliteMigrationManager : NSObject 35 | 36 | @end 37 | 38 | /* Provide a compatibility alias for the legacy class name. */ 39 | DEPRECATED_ATTRIBUTE @interface PLSqliteMigrationVersionManager : PLSqliteMigrationManager 40 | @end -------------------------------------------------------------------------------- /Classes/PLSqliteMigrationManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLSqliteMigrationManager.h" 31 | 32 | /** 33 | * @deprecated Replaced by PLSqliteMigrationManager. 34 | */ 35 | @implementation PLSqliteMigrationVersionManager @end 36 | 37 | /** 38 | * Implements database schema versioning using SQLite's per-database user_version field 39 | * (see http://www.sqlite.org/pragma.html#version). 40 | * 41 | * Additionally implements the requisite SQLite locking. 42 | * 43 | * @warning This class depends on, and modifies, the SQLite per-database user_version 44 | * field. Users of this class must not modify or rely upon the value of the user_version. This class 45 | * will not correctly function if the user_version is externally modified. 46 | * 47 | * @par Thread Safety 48 | * PLSqliteMigrationVersionManager instances implement no locking and must not be shared between threads. 49 | */ 50 | @implementation PLSqliteMigrationManager 51 | 52 | // from PLDatabaseMigrationVersionManager protocol 53 | - (BOOL) beginExclusiveTransactionForDatabase: (id) database error: (NSError **) outError { 54 | return [database executeUpdateAndReturnError: outError statement: @"BEGIN EXCLUSIVE TRANSACTION"]; 55 | } 56 | 57 | 58 | // from PLDatabaseMigrationVersionManager protocol 59 | - (BOOL) rollbackTransactionForDatabase: (id) database error: (NSError **) outError { 60 | return [database rollbackTransactionAndReturnError: outError]; 61 | } 62 | 63 | 64 | // from PLDatabaseMigrationVersionManager protocol 65 | - (BOOL) commitTransactionForDatabase: (id) database error: (NSError **) outError { 66 | return [database commitTransactionAndReturnError: outError]; 67 | } 68 | 69 | 70 | // from PLDatabaseMigrationVersionManager protocol 71 | - (BOOL) version: (int *) version forDatabase: (id) database error: (NSError **) outError { 72 | id rs; 73 | 74 | assert(version != NULL); 75 | 76 | /* Execute the query */ 77 | rs = [database executeQueryAndReturnError: outError statement: @"PRAGMA user_version"]; 78 | if (rs == nil) 79 | return NO; 80 | 81 | /* Get the version */ 82 | BOOL hasNext = [rs next]; 83 | assert(hasNext == YES); // Should not happen 84 | *version = [rs intForColumn: @"user_version"]; 85 | [rs close]; 86 | 87 | return YES; 88 | } 89 | 90 | 91 | // from PLDatabaseMigrationVersionManager protocol 92 | - (BOOL) setVersion: (int) version forDatabase: (id) database error: (NSError **) outError { 93 | /* NOTE! We use stringWithFormat because: 94 | * A) It's safe (only inserting an integer) 95 | * B) Pragma doesn't seem to work with prepared statements. 96 | * 97 | * This is not a pattern to follow -- ALWAYS use prepared statements and 98 | * save yourself from being the 5 billionth person to create a SQL injection 99 | * attack vector. 100 | */ 101 | return [database executeUpdateAndReturnError: outError statement: [NSString stringWithFormat: @"PRAGMA user_version = %d", version]]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Classes/PLSqliteMigrationManagerTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import 32 | 33 | #import "PLSqliteDatabase.h" 34 | #import "PLSqliteMigrationManager.h" 35 | 36 | @interface PLSqliteMigrationManagerTests : SenTestCase { 37 | @private 38 | NSString *_dbPath; 39 | PLSqliteDatabase *_db; 40 | PLSqliteMigrationManager *_versionManager; 41 | } 42 | @end 43 | 44 | 45 | @implementation PLSqliteMigrationManagerTests 46 | 47 | - (void) setUp { 48 | /* Create a temporary file for the database. Secure -- user owns enclosing directory. */ 49 | _dbPath = [[NSTemporaryDirectory() stringByAppendingPathComponent: [[NSProcessInfo processInfo] globallyUniqueString]] retain]; 50 | 51 | /* Create the temporary database */ 52 | _db = [[PLSqliteDatabase alloc] initWithPath: _dbPath]; 53 | STAssertTrue([_db open], @"Could not open temporary database"); 54 | 55 | /* Create the version manager */ 56 | _versionManager = [[PLSqliteMigrationManager alloc] init]; 57 | } 58 | 59 | - (void) tearDown { 60 | /* Close the open database file */ 61 | [_db close]; 62 | 63 | /* Remove the temporary database file */ 64 | STAssertTrue([[NSFileManager defaultManager] removeItemAtPath: _dbPath error: NULL], @"Could not clean up database %@", _dbPath); 65 | 66 | /* Release our objects */ 67 | [_dbPath release]; 68 | [_db release]; 69 | [_versionManager release]; 70 | } 71 | 72 | - (void) testDefaultVersion { 73 | NSError *error; 74 | int version; 75 | 76 | STAssertTrue([_versionManager version: &version forDatabase: _db error: &error], @"Could not retrieve version: %@", error); 77 | STAssertEquals(0, version, @"Default version should be 0, is %d", version); 78 | } 79 | 80 | - (void) testSetGetVersion { 81 | NSError *error; 82 | int version; 83 | 84 | STAssertTrue([_versionManager setVersion: 5 forDatabase: _db error: &error], @"Could not set version: %@", error); 85 | STAssertTrue([_versionManager version: &version forDatabase: _db error: &error], @"Could not retrieve version: %@", error); 86 | STAssertEquals(5, version, @"Version should be 5, is %d", version); 87 | } 88 | 89 | - (void) testBeginAndRollbackTransaction { 90 | NSError *error; 91 | 92 | STAssertTrue([_versionManager beginExclusiveTransactionForDatabase: _db error: &error], @"Could not start a transaction: %@", error); 93 | 94 | STAssertTrue([_db executeUpdate: @"CREATE TABLE test (a int)"], @"Could not create test table"); 95 | STAssertTrue(([_db executeUpdate: @"INSERT INTO test (a) VALUES (?)", [NSNumber numberWithInt: 42]]), @"Inserting test data failed"); 96 | 97 | STAssertTrue([_db tableExists: @"test"], @"Table was not created"); 98 | STAssertTrue([_versionManager rollbackTransactionForDatabase: _db error: &error], @"Could not roll back"); 99 | STAssertFalse([_db tableExists: @"test"], @"Table was not rolled back"); 100 | } 101 | 102 | 103 | - (void) testBeginAndCommitTransaction { 104 | NSError *error; 105 | 106 | STAssertTrue([_versionManager beginExclusiveTransactionForDatabase: _db error: &error], @"Could not start a transaction: %@", error); 107 | 108 | STAssertTrue([_db executeUpdate: @"CREATE TABLE test (a int)"], @"Could not create test table"); 109 | STAssertTrue(([_db executeUpdate: @"INSERT INTO test (a) VALUES (?)", [NSNumber numberWithInt: 42]]), @"Inserting test data failed"); 110 | STAssertTrue([_db tableExists: @"test"], @"Table was not created"); 111 | 112 | STAssertTrue([_versionManager commitTransactionForDatabase: _db error: &error], @"Could not commit transaction"); 113 | 114 | STAssertTrue([_db tableExists: @"test"], @"Table was not comitted"); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /Classes/PLSqlitePreparedStatement.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2010 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #if PL_DB_PRIVATE 31 | 32 | #import 33 | 34 | #import "PLSqliteDatabase.h" 35 | #import "PLSqliteResultSet.h" 36 | 37 | @interface PLSqlitePreparedStatement : NSObject { 38 | @private 39 | /** Our backing database. */ 40 | PLSqliteDatabase *_database; 41 | 42 | /** Cache in which the statement should be checked in on close, or nil if no cache is available and the 43 | * statement should simply be finalized. */ 44 | PLSqliteStatementCache *_statementCache; 45 | 46 | /** The prepared SQLite statement. */ 47 | sqlite3_stmt *_sqlite_stmt; 48 | 49 | /** The unprepared query string. */ 50 | NSString *_queryString; 51 | 52 | /** Number of parameters. */ 53 | int _parameterCount; 54 | 55 | /** Is the prepared statement in use by a PLResultSet */ 56 | BOOL _inUse; 57 | 58 | /** If YES, the prepared statement is closed when the first result set is checked in. */ 59 | BOOL _closeAtCheckin; 60 | } 61 | 62 | - (id) initWithDatabase: (PLSqliteDatabase *) db 63 | statementCache: (PLSqliteStatementCache *) statementCache 64 | sqliteStmt: (sqlite3_stmt *) sqlite_stmt 65 | queryString: (NSString *) queryString 66 | closeAtCheckin: (BOOL) closeAtCheckin; 67 | 68 | - (void) populateError: (NSError **) error withErrorCode: (PLDatabaseError) errorCode description: (NSString *) localizedDescription; 69 | 70 | // DO NOT CALL. Must only be called from PLSqliteResultSet 71 | - (void) checkinResultSet: (PLSqliteResultSet *) resultSet; 72 | 73 | /** The prepared statement's backing database. */ 74 | @property(nonatomic, readonly) PLSqliteDatabase *database; 75 | 76 | @end 77 | 78 | #endif -------------------------------------------------------------------------------- /Classes/PLSqlitePreparedStatementTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqlitePreparedStatement.h" 33 | 34 | @interface PLSqlitePreparedStatementTests : SenTestCase { 35 | @private 36 | PLSqliteDatabase *_db; 37 | } 38 | 39 | @end 40 | 41 | @implementation PLSqlitePreparedStatementTests 42 | 43 | - (void) setUp { 44 | _db = [[PLSqliteDatabase alloc] initWithPath: @":memory:"]; 45 | STAssertTrue([_db open], @"Couldn't open the test database"); 46 | 47 | STAssertTrue([_db executeUpdate: @"CREATE TABLE test (" 48 | "id INTEGER PRIMARY KEY AUTOINCREMENT," 49 | "name VARCHAR(255)," 50 | "color VARCHAR(255))"], 51 | @"Could not create test table"); 52 | } 53 | 54 | - (void) tearDown { 55 | [_db release]; 56 | } 57 | 58 | - (void) testParameterCount { 59 | id stmt; 60 | 61 | stmt = [_db prepareStatement: @"SELECT * FROM test WHERE name = (?)"]; 62 | STAssertEquals(1, [stmt parameterCount], @"Incorrect parameter count"); 63 | [stmt close]; 64 | } 65 | 66 | - (void) testInUseHandling { 67 | id stmt; 68 | id rs; 69 | 70 | /* Prepare the statement */ 71 | stmt = [_db prepareStatement: @"SELECT * FROM test WHERE name = ?"]; 72 | [stmt bindParameters: [NSArray arrayWithObjects: @"Johnny", nil]]; 73 | 74 | /* First result set */ 75 | rs = [stmt executeQuery]; 76 | 77 | /* Should throw an exception */ 78 | STAssertThrows([stmt executeQuery], @"Did not throw an exception re-executing query"); 79 | STAssertThrows(([stmt bindParameters: [NSArray arrayWithObjects: @"Johnny", nil]]), 80 | @"Did not throw an exception re-binding query"); 81 | 82 | /* Close and try again (should not throw an exception) */ 83 | [rs close]; 84 | [[stmt executeQuery] close]; 85 | } 86 | 87 | - (void) testClose { 88 | id stmt; 89 | 90 | stmt = [_db prepareStatement: @"SELECT * FROM test WHERE name = ?"]; 91 | [stmt close]; 92 | STAssertThrows([stmt executeQuery], @"Did not throw an exception executing query on closed statement"); 93 | } 94 | 95 | 96 | /* Test basic update and query support */ 97 | - (void) testUpdateAndQuery { 98 | id stmt; 99 | id rs; 100 | 101 | /* Prepare the statement */ 102 | stmt = [_db prepareStatement: @"INSERT INTO test (name, color) VALUES (?, ?)"]; 103 | 104 | /* Insert twice */ 105 | [stmt bindParameters: [NSArray arrayWithObjects: @"Johnny", @"blue", nil]]; 106 | STAssertTrue([stmt executeUpdate], @"INSERT failed"); 107 | 108 | [stmt bindParameters: [NSArray arrayWithObjects: @"Sarah", @"red", nil]]; 109 | STAssertTrue([stmt executeUpdate], @"INSERT failed"); 110 | 111 | /* Now check the values */ 112 | stmt = [_db prepareStatement: @"SELECT * FROM test WHERE name = ?"]; 113 | 114 | /* Johnny */ 115 | [stmt bindParameters: [NSArray arrayWithObjects: @"Johnny", nil]]; 116 | rs = [stmt executeQuery]; 117 | 118 | STAssertNotNil(rs, @"Query failed"); 119 | STAssertTrue([rs next], @"No results returned"); 120 | 121 | STAssertFalse([rs isNullForColumn: @"id"], @"ID column wasn't set"); 122 | STAssertTrue([@"Johnny" isEqual: [rs stringForColumn: @"name"]], @"Name set incorrectly"); 123 | STAssertTrue([@"blue" isEqual: [rs stringForColumn: @"color"]], @"Color set incorrectly"); 124 | [rs close]; 125 | 126 | /* Sarah */ 127 | [stmt bindParameters: [NSArray arrayWithObjects: @"Sarah", nil]]; 128 | rs = [stmt executeQuery]; 129 | 130 | STAssertNotNil(rs, @"Query failed"); 131 | STAssertTrue([rs next], @"No results returned"); 132 | 133 | STAssertFalse([rs isNullForColumn: @"id"], @"ID column wasn't set"); 134 | STAssertTrue([@"Sarah" isEqual: [rs stringForColumn: @"name"]], @"Name set incorrectly"); 135 | STAssertTrue([@"red" isEqual: [rs stringForColumn: @"color"]], @"Color set incorrectly"); 136 | 137 | [rs close]; 138 | [stmt close]; 139 | } 140 | 141 | 142 | /* Test dictionary-based binding */ 143 | - (void) testBindParameterDictionary { 144 | id stmt; 145 | NSMutableDictionary *parameters; 146 | 147 | /* Prepare the statement */ 148 | stmt = [_db prepareStatement: @"INSERT INTO test (name, color) VALUES (:name, :color)"]; 149 | 150 | /* Create the parameter dictionary */ 151 | parameters = [NSMutableDictionary dictionaryWithCapacity: 2]; 152 | [parameters setObject: @"Appleseed" forKey: @"name"]; 153 | [parameters setObject: @"blue" forKey: @"color"]; 154 | 155 | /* Bind and insert our values */ 156 | [stmt bindParameterDictionary: parameters]; 157 | STAssertTrue([stmt executeUpdate], @"INSERT failed"); 158 | 159 | /* Fetch the inserted data */ 160 | id rs = [_db executeQuery: @"SELECT * FROM test WHERE color = ?", @"blue"]; 161 | STAssertTrue([rs next], @"No data returned"); 162 | STAssertTrue([@"Appleseed" isEqual: [rs stringForColumn: @"name"]], @"Name incorrectly bound"); 163 | STAssertTrue([@"blue" isEqual: [rs stringForColumn: @"color"]], @"Color incorrectly bound"); 164 | 165 | [rs close]; 166 | [stmt close]; 167 | } 168 | 169 | 170 | /* Test handling of all supported parameter data types */ 171 | - (void) testBindParameters { 172 | id stmt; 173 | id rs; 174 | 175 | /* Create the data table */ 176 | STAssertTrue([_db executeUpdate: @"CREATE TABLE data (" 177 | "intval int," 178 | "int64val int," 179 | "int64val_negative int," 180 | "stringval varchar(30)," 181 | "nilval int," 182 | "floatval float," 183 | "doubleval double precision," 184 | "dateval double precision," 185 | "dataval blob" 186 | ")"], @"Could not create table"); 187 | 188 | /* Prepare the insert statement */ 189 | stmt = [_db prepareStatement: @"INSERT INTO data (intval, int64val, int64val_negative, stringval, nilval, floatval, doubleval, dateval, dataval)" 190 | "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"]; 191 | STAssertNotNil(stmt, @"Could not create statement"); 192 | 193 | /* Some example data */ 194 | NSDate *now = [NSDate date]; 195 | const char bytes[] = "This is some example test data"; 196 | NSData *data = [NSData dataWithBytes: bytes length: sizeof(bytes)]; 197 | 198 | /* Create our parameter list */ 199 | NSArray *values = [NSArray arrayWithObjects: 200 | [NSNumber numberWithInt: 42], 201 | [NSNumber numberWithLongLong: INT64_MAX], 202 | [NSNumber numberWithLongLong: INT64_MIN], 203 | @"test", 204 | [NSNull null], 205 | [NSNumber numberWithFloat: 3.14], 206 | [NSNumber numberWithDouble: 3.14159], 207 | now, 208 | data, 209 | nil 210 | ]; 211 | 212 | /* Bind our values */ 213 | [stmt bindParameters: values]; 214 | 215 | /* Execute the update */ 216 | STAssertTrue([stmt executeUpdate], @"INSERT failed"); 217 | 218 | /* Execute the query */ 219 | rs = [_db executeQuery: @"SELECT * FROM data WHERE intval = 42"]; 220 | STAssertNotNil(rs, @"Query failed"); 221 | STAssertTrue([rs next], @"No rows returned"); 222 | 223 | /* NULL value */ 224 | STAssertTrue([rs isNullForColumn: @"nilval"], @"NULL value not returned."); 225 | 226 | /* Date value */ 227 | STAssertEquals([now timeIntervalSince1970], [[rs dateForColumn: @"dateval"] timeIntervalSince1970], @"Date value incorrect."); 228 | 229 | /* String */ 230 | STAssertTrue([@"test" isEqual: [rs stringForColumn: @"stringval"]], @"String value incorrect."); 231 | 232 | /* Integer */ 233 | STAssertEquals(42, [rs intForColumn: @"intval"], @"Integer value incorrect."); 234 | 235 | /* 64-bit integer value */ 236 | STAssertEquals(INT64_MAX, [rs bigIntForColumn: @"int64val"], @"64-bit integer value incorrect"); 237 | 238 | /* Negative 64-bit integer value */ 239 | STAssertEquals(INT64_MIN, [rs bigIntForColumn: @"int64val_negative"], @"64-bit negative integer value incorrect"); 240 | 241 | /* Float */ 242 | STAssertEquals(3.14f, [rs floatForColumn: @"floatval"], @"Float value incorrect"); 243 | 244 | /* Double */ 245 | STAssertEquals(3.14159, [rs doubleForColumn: @"doubleval"], @"Double value incorrect"); 246 | 247 | /* Data */ 248 | STAssertTrue([data isEqualToData: [rs dataForColumn: @"dataval"]], @"Data value incorrect"); 249 | 250 | [rs close]; 251 | [stmt close]; 252 | } 253 | 254 | /** 255 | * Test handling of binding too many parameters. This can occur with a dictionary containing unused keys. 256 | */ 257 | - (void) testBindTooManyParameters { 258 | id stmt; 259 | NSMutableDictionary *parameters; 260 | 261 | /* Prepare the statement */ 262 | stmt = [_db prepareStatement: @"INSERT INTO test (name, color) VALUES (:name, :color)"]; 263 | 264 | /* Create the parameter dictionary */ 265 | parameters = [NSMutableDictionary dictionaryWithCapacity: 2]; 266 | [parameters setObject: @"Appleseed" forKey: @"name"]; 267 | [parameters setObject: @"blue" forKey: @"color"]; 268 | [parameters setObject: @"value" forKey: @"extra"]; 269 | 270 | /* Bind and insert our values. If no exception is thrown, the test succeeds. */ 271 | [stmt bindParameterDictionary: parameters]; 272 | [stmt close]; 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /Classes/PLSqliteResultSet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifdef PL_DB_PRIVATE 31 | 32 | #import 33 | #import 34 | 35 | #import "PLResultSet.h" 36 | 37 | @class PLSqlitePreparedStatement; 38 | 39 | @interface PLSqliteResultSet : NSObject { 40 | @private 41 | /** The prepared statement */ 42 | PLSqlitePreparedStatement *_stmt; 43 | 44 | /** The weak referenced sqlite3 prepared statement. */ 45 | sqlite3_stmt *_sqlite_stmt; 46 | 47 | /** The number of columns in the result. */ 48 | uint32_t _columnCount; 49 | 50 | /** Cache of column name to column index. This value is lazy initialized and may be NULL. */ 51 | NSDictionary *_columnNames; 52 | } 53 | 54 | - (id) initWithPreparedStatement: (PLSqlitePreparedStatement *) stmt sqliteStatemet: (sqlite3_stmt *)sqlite_stmt; 55 | 56 | /** Return YES if the result set has been closed, NO otherwise. Exposed to support the PLResultSet unit tests. */ 57 | @property(nonatomic, readonly, getter=isClosed) BOOL closed; 58 | 59 | @end 60 | 61 | #import "PLSqlitePreparedStatement.h" 62 | 63 | #endif -------------------------------------------------------------------------------- /Classes/PLSqliteResultSet.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLSqliteResultSet.h" 31 | #import "PLSqliteUnlockNotify.h" 32 | 33 | /** 34 | * @internal 35 | * 36 | * SQLite #PLResultSet implementation. 37 | * 38 | * @par Thread Safety 39 | * PLSqliteResultSet instances implement no locking and must not be shared between threads 40 | * without external synchronization. 41 | */ 42 | @implementation PLSqliteResultSet 43 | 44 | /** 45 | * Initialize the ResultSet with an open database and an sqlite3 prepare statement. 46 | * 47 | * MEMORY OWNERSHIP WARNING: 48 | * We are passed an sqlite3_stmt reference owned by the PLSqlitePreparedStatement. 49 | * It will remain valid insofar as the PLSqlitePreparedStatement reference is retained. 50 | * 51 | * @par Designated Initializer 52 | * This method is the designated initializer for the PLSqliteResultSet class. 53 | */ 54 | - (id) initWithPreparedStatement: (PLSqlitePreparedStatement *) stmt 55 | sqliteStatemet: (sqlite3_stmt *) sqlite_stmt 56 | { 57 | if ((self = [super init]) == nil) { 58 | return nil; 59 | } 60 | 61 | /* Save our database and statement references. */ 62 | _stmt = [stmt retain]; 63 | _sqlite_stmt = sqlite_stmt; 64 | 65 | /* Save result information */ 66 | _columnCount = sqlite3_column_count(_sqlite_stmt); 67 | 68 | return self; 69 | } 70 | 71 | - (void) dealloc { 72 | /* 'Check in' our prepared statement reference */ 73 | [self close]; 74 | 75 | /* Release the column cache. */ 76 | if (_columnNames != NULL) 77 | [_columnNames release]; 78 | 79 | /* Release the statement. */ 80 | [_stmt release]; 81 | 82 | [super dealloc]; 83 | } 84 | 85 | // property getter 86 | - (BOOL) isClosed { 87 | if (_sqlite_stmt == NULL) 88 | return YES; 89 | 90 | return NO; 91 | } 92 | 93 | // From PLResultSet 94 | - (void) close { 95 | if (_sqlite_stmt == NULL) 96 | return; 97 | 98 | /* Check ourselves back in and give up our statement reference */ 99 | [_stmt checkinResultSet: self]; 100 | _sqlite_stmt = NULL; 101 | } 102 | 103 | /** 104 | * @internal 105 | * Assert that the result set has not been closed 106 | */ 107 | - (void) assertNotClosed { 108 | if (_sqlite_stmt == NULL) 109 | [NSException raise: PLSqliteException format: @"Attempt to access already-closed result set."]; 110 | } 111 | 112 | /* From PLResultSet */ 113 | - (BOOL) next { 114 | [self assertNotClosed]; 115 | 116 | if ([self nextAndReturnError: NULL] == PLResultSetStatusRow) 117 | return YES; 118 | 119 | /* This depreciated API can not differentiate between an error or 120 | * the end of the result set. */ 121 | return NO; 122 | } 123 | 124 | - (PLResultSetStatus) nextAndReturnError: (NSError **) error { 125 | [self assertNotClosed]; 126 | 127 | int ret = pl_sqlite3_blocking_step(_sqlite_stmt); 128 | 129 | /* Inform the database of deadlock status */ 130 | if (ret == SQLITE_BUSY || ret == SQLITE_LOCKED) { 131 | [_stmt.database setTxBusy]; 132 | } else { 133 | [_stmt.database resetTxBusy]; 134 | } 135 | 136 | /* No more rows available. */ 137 | if (ret == SQLITE_DONE) 138 | return PLResultSetStatusDone; 139 | 140 | /* A row is available */ 141 | if (ret == SQLITE_ROW) 142 | return PLResultSetStatusRow; 143 | 144 | 145 | 146 | /* An error has occured */ 147 | [_stmt populateError: error withErrorCode: PLDatabaseErrorQueryFailed description: NSLocalizedString(@"Could not retrieve the next result row", @"Generic result row error")]; 148 | return PLResultSetStatusError; 149 | } 150 | 151 | /* From PLResultSet */ 152 | - (BOOL) enumerateWithBlock: (void (^)(id rs, BOOL *stop)) block { 153 | return [self enumerateAndReturnError: NULL block: block]; 154 | } 155 | 156 | /* From PLResultSet */ 157 | - (BOOL) enumerateAndReturnError: (NSError **) outError block: (void (^)(id rs, BOOL *stop)) block { 158 | BOOL stop = NO; 159 | 160 | /* Iterate over the results */ 161 | PLResultSetStatus rss; 162 | while ((rss = [self nextAndReturnError: outError]) == PLResultSetStatusRow) { 163 | block(self, &stop); 164 | if (stop) 165 | break; 166 | } 167 | 168 | /* Handle completion invariant; the result set is expected to implicitly close when 169 | * all rows are iterated and *stop is not set. */ 170 | if (stop == NO && rss == PLResultSetStatusDone) { 171 | [self close]; 172 | } 173 | 174 | /* Handle error completion invariant. If an error occurs, the result set is expected 175 | * to implicitly close */ 176 | if (rss == PLResultSetStatusError) { 177 | [self close]; 178 | return NO; 179 | } 180 | 181 | return YES; 182 | } 183 | 184 | 185 | /* From PLResultSet */ 186 | - (int) columnIndexForName: (NSString *) name { 187 | [self assertNotClosed]; 188 | 189 | /* Lazy initialization of the column name mapping. Profiling demonstrates that iPhone code doing a high 190 | * volume of queries avoids a significant performance penalty if the column name mapping is not used. */ 191 | if (_columnNames == nil) { 192 | /* Create a column name cache */ 193 | NSMutableDictionary *columnNames = [[NSMutableDictionary alloc] initWithCapacity: _columnCount]; 194 | _columnNames = columnNames; 195 | 196 | for (int columnIndex = 0; columnIndex < _columnCount; columnIndex++) { 197 | /* Fetch the name */ 198 | NSString *name = [NSString stringWithUTF8String: sqlite3_column_name(_sqlite_stmt, columnIndex)]; 199 | 200 | /* Set the dictionary value */ 201 | [columnNames setObject: [NSNumber numberWithInt: columnIndex] forKey: [name lowercaseString]]; 202 | } 203 | } 204 | 205 | NSString *key = [name lowercaseString]; 206 | NSNumber *idx = [_columnNames objectForKey: key]; 207 | if (idx != nil) { 208 | return [idx intValue]; 209 | } 210 | 211 | /* Not found */ 212 | [NSException raise: PLSqliteException format: @"Attempted to access unknown result column %@", name]; 213 | 214 | /* Unreachable */ 215 | abort(); 216 | } 217 | 218 | 219 | /** 220 | * @internal 221 | * Validate the column index and return the column type 222 | */ 223 | - (int) validateColumnIndex: (int) columnIndex { 224 | [self assertNotClosed]; 225 | 226 | int columnType; 227 | 228 | /* Verify that the index is in range */ 229 | if (columnIndex > _columnCount - 1 || columnIndex < 0) 230 | [NSException raise: PLSqliteException format: @"Attempted to access out-of-range column index %d", columnIndex]; 231 | 232 | /* Fetch the type */ 233 | columnType = sqlite3_column_type(_sqlite_stmt, columnIndex); 234 | 235 | return columnType; 236 | } 237 | 238 | /* This beauty generates the PLResultSet value accessors for a given data type */ 239 | #define VALUE_ACCESSORS(ReturnType, MethodName, Expression) \ 240 | - (ReturnType) MethodName ## ForColumnIndex: (int) columnIndex { \ 241 | [self assertNotClosed]; \ 242 | int columnType = [self validateColumnIndex: columnIndex]; \ 243 | \ 244 | /* Quiesce unused variable warning */ \ 245 | if (YES || columnType == SQLITE_NULL) \ 246 | return (Expression); \ 247 | } \ 248 | \ 249 | - (ReturnType) MethodName ## ForColumn: (NSString *) column { \ 250 | return [self MethodName ## ForColumnIndex: [self columnIndexForName: column]]; \ 251 | } 252 | 253 | /* bool */ 254 | VALUE_ACCESSORS(BOOL, bool, sqlite3_column_int(_sqlite_stmt, columnIndex)) 255 | 256 | /* int32_t */ 257 | VALUE_ACCESSORS(int32_t, int, sqlite3_column_int(_sqlite_stmt, columnIndex)) 258 | 259 | /* int64_t */ 260 | VALUE_ACCESSORS(int64_t, bigInt, sqlite3_column_int64(_sqlite_stmt, columnIndex)) 261 | 262 | /* date */ 263 | VALUE_ACCESSORS(NSDate *, date, columnType == SQLITE_NULL ? nil : 264 | [NSDate dateWithTimeIntervalSince1970: sqlite3_column_double(_sqlite_stmt, columnIndex)]) 265 | 266 | /* string */ 267 | VALUE_ACCESSORS(NSString *, string, columnType == SQLITE_NULL ? nil : 268 | [NSString stringWithCharacters: sqlite3_column_text16(_sqlite_stmt, columnIndex) 269 | length: sqlite3_column_bytes16(_sqlite_stmt, columnIndex) / 2]) 270 | 271 | /* float */ 272 | VALUE_ACCESSORS(float, float, sqlite3_column_double(_sqlite_stmt, columnIndex)) 273 | 274 | /* double */ 275 | VALUE_ACCESSORS(double, double, sqlite3_column_double(_sqlite_stmt, columnIndex)) 276 | 277 | /* data */ 278 | VALUE_ACCESSORS(NSData *, data, columnType == SQLITE_NULL ? nil : 279 | [NSData dataWithBytes: sqlite3_column_blob(_sqlite_stmt, columnIndex) 280 | length: sqlite3_column_bytes(_sqlite_stmt, columnIndex)]) 281 | 282 | 283 | /* From PLResultSet */ 284 | - (id) objectForColumnIndex: (int) columnIndex { 285 | [self assertNotClosed]; 286 | 287 | int columnType = [self validateColumnIndex: columnIndex]; 288 | switch (columnType) { 289 | case SQLITE_TEXT: 290 | return [self stringForColumnIndex: columnIndex]; 291 | 292 | case SQLITE_INTEGER: 293 | return [NSNumber numberWithLongLong: [self bigIntForColumnIndex: columnIndex]]; 294 | 295 | case SQLITE_FLOAT: 296 | return [NSNumber numberWithDouble: [self doubleForColumnIndex: columnIndex]]; 297 | 298 | case SQLITE_BLOB: 299 | return [self dataForColumnIndex: columnIndex]; 300 | 301 | case SQLITE_NULL: 302 | return nil; 303 | 304 | default: 305 | [NSException raise: PLDatabaseException format: @"Unhandled SQLite column type %d", columnType]; 306 | } 307 | 308 | /* Unreachable */ 309 | abort(); 310 | } 311 | 312 | 313 | /* From PLResultSet */ 314 | - (id) objectForColumn: (NSString *) columnName { 315 | return [self objectForColumnIndex: [self columnIndexForName: columnName]]; 316 | } 317 | 318 | /* From PLResultSet */ 319 | - (id) objectForKeyedSubscript: (id)key { 320 | return [self objectForColumn: key]; 321 | } 322 | 323 | /* From PLResultSet */ 324 | - (id) objectAtIndexedSubscript: (NSUInteger) index { 325 | return [self objectForColumnIndex: (int) index]; 326 | } 327 | 328 | 329 | /* from PLResultSet */ 330 | - (BOOL) isNullForColumnIndex: (int) columnIndex { 331 | [self assertNotClosed]; 332 | 333 | int columnType = [self validateColumnIndex: columnIndex]; 334 | 335 | /* If the column has a null value, return YES. */ 336 | if (columnType == SQLITE_NULL) 337 | return YES; 338 | 339 | /* Return NO for all other column types. */ 340 | return NO; 341 | } 342 | 343 | /* from PLResultSet */ 344 | - (BOOL) isNullForColumn: (NSString *) columnName { 345 | return [self isNullForColumnIndex: [self columnIndexForName: columnName]]; 346 | } 347 | 348 | @end 349 | -------------------------------------------------------------------------------- /Classes/PLSqliteStatementCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import 32 | 33 | #ifndef PL_DB_PRIVATE 34 | @class PLSqliteStatementCache; 35 | #else 36 | 37 | #import 38 | 39 | @interface PLSqliteStatementCache : NSObject { 40 | @private 41 | /** Maximum size. */ 42 | NSUInteger _capacity; 43 | 44 | /** Current size. */ 45 | NSUInteger _size; 46 | 47 | /** Maps the query string to a CFMutableArrayRef containing sqlite3_stmt instances. We claim ownership for these statements. */ 48 | NSMutableDictionary *_availableStatements; 49 | 50 | /** All live statements (whether or not they're checked out). */ 51 | __strong CFMutableSetRef _allStatements; 52 | 53 | /** Internal lock. Must be held when mutating state. */ 54 | OSSpinLock _lock; 55 | } 56 | 57 | - (id) initWithCapacity: (NSUInteger) capacity; 58 | 59 | - (void) close; 60 | 61 | - (void) registerStatement: (sqlite3_stmt *) stmt; 62 | 63 | - (void) checkinStatement: (sqlite3_stmt *) stmt forQuery: (NSString *) query; 64 | 65 | - (sqlite3_stmt *) checkoutStatementForQueryString: (NSString *) query; 66 | 67 | - (void) removeAllStatements; 68 | 69 | @end 70 | 71 | #endif /* PL_DB_PRIVATE */ -------------------------------------------------------------------------------- /Classes/PLSqliteStatementCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PLSqliteStatementCache.h" 31 | 32 | static const CFArrayCallBacks StatementCacheArrayCallbacks = { 33 | .version = 0, 34 | .retain = NULL, 35 | .release = NULL, 36 | .copyDescription = NULL, 37 | .equal = NULL 38 | }; 39 | 40 | static const CFSetCallBacks StatementCacheSetCallbacks = { 41 | .version = 0, 42 | .retain = NULL, 43 | .release = NULL, 44 | .copyDescription = NULL, 45 | .equal = NULL, 46 | .hash = NULL 47 | }; 48 | 49 | @interface PLSqliteStatementCache (PrivateMethods) 50 | - (void) removeAllStatementsHasLock: (BOOL) locked; 51 | @end 52 | 53 | /** 54 | * @internal 55 | * 56 | * Manages a cache of sqlite3_stmt instances, providing a mapping from query string to a sqlite3_stmt. 57 | * 58 | * The implementation naively evicts all statements should the cache become full. Future enhancement 59 | * may include implementing an LRU strategy, but it's not expected that many users will be executing 60 | * more unique queries than will fit in a reasonably sized cache. 61 | * 62 | * @par Thread Safety 63 | * 64 | * Unlike most classes in this library, PLSqliteStatementCache is thread-safe; this is intended to allow the safe 65 | * finalization/deallocation of SQLite ojects from multiple threads. 66 | * 67 | * Note, however, that the implementation is optimized for minimal contention. 68 | */ 69 | @implementation PLSqliteStatementCache 70 | 71 | /** 72 | * Initialize the cache with the provided query @a capacity. The cache will discard sqlite3_stmt instances to stay 73 | * within the given capacity. 74 | * 75 | * @param capacity Maximum cache capacity. 76 | */ 77 | - (id) initWithCapacity: (NSUInteger) capacity { 78 | if ((self = [super init]) == nil) 79 | return nil; 80 | 81 | _capacity = capacity; 82 | _availableStatements = [[NSMutableDictionary alloc] init]; 83 | _allStatements = CFSetCreateMutable(NULL, 0, &StatementCacheSetCallbacks); 84 | 85 | _lock = OS_SPINLOCK_INIT; 86 | 87 | return self; 88 | } 89 | 90 | - (void) dealloc { 91 | /* The sqlite statements have to be finalized explicitly */ 92 | [self close]; 93 | 94 | [_availableStatements release]; 95 | 96 | if (_allStatements != NULL) { 97 | CFRelease(_allStatements); 98 | _allStatements = NULL; 99 | } 100 | 101 | [super dealloc]; 102 | } 103 | 104 | /** 105 | * Register a sqlite3 prepared statement for the given @a query. This is used to support properly ordered finalization 106 | * of both sqlite3_stmt references and the backing sqlite3 database; all statements that will be checked out 107 | * via checkoutStatementForQueryString: must be registered apriori. 108 | * 109 | * @param stmt The statement to register. 110 | * @param query The query string corresponding to this statement. 111 | * 112 | */ 113 | - (void) registerStatement: (sqlite3_stmt *) stmt { 114 | OSSpinLockLock(&_lock); { 115 | CFSetAddValue(_allStatements, stmt); 116 | }; OSSpinLockUnlock(&_lock); 117 | } 118 | 119 | /** 120 | * Check in an sqlite3 prepared statement for the given @a query, making it available for re-use from the cache. 121 | * The statement will be reset (via sqlite3_reset()). 122 | * 123 | * @param stmt The statement to check in. 124 | * @param query The query string corresponding to this statement. 125 | * 126 | * @warning MEMORY OWNERSHIP WARNING: The receiver will claim ownership of the statement object. 127 | */ 128 | - (void) checkinStatement: (sqlite3_stmt *) stmt forQuery: (NSString *) query { 129 | OSSpinLockLock(&_lock); { 130 | /* If the statement pointer is not currently registered, there's nothing to do here. This should never occur. */ 131 | if (!CFSetContainsValue(_allStatements, stmt)) { 132 | // TODO - Should this be an assert()? 133 | NSLog(@"[PLSqliteStatementCache]: Received an unknown statement %p during check-in.", stmt); 134 | OSSpinLockUnlock(&_lock); 135 | return; 136 | } 137 | 138 | /* Bump the count */ 139 | _size++; 140 | if (_size > _capacity) { 141 | [self removeAllStatementsHasLock: YES]; 142 | } 143 | 144 | /* Fetch the statement set for this query */ 145 | CFMutableArrayRef stmtArray = (CFMutableArrayRef) [_availableStatements objectForKey: query]; 146 | if (stmtArray == nil) { 147 | stmtArray = CFArrayCreateMutable(NULL, 0, &StatementCacheArrayCallbacks); 148 | [_availableStatements setObject: (id) stmtArray forKey: query]; 149 | [(id)stmtArray release]; 150 | } 151 | 152 | /* Claim ownership of the statement */ 153 | sqlite3_reset(stmt); 154 | CFArrayAppendValue(stmtArray, stmt); 155 | }; OSSpinLockUnlock(&_lock); 156 | } 157 | 158 | /** 159 | * Check out a sqlite3 prepared statement for the given @a query, or NULL if none is cached. 160 | * 161 | * @param query The query string corresponding to this statement. 162 | * 163 | * @warning MEMORY OWNERSHIP WARNING: The caller is given ownership of the statement object, and MUST either deallocate 164 | * that object or provide it to PLSqliteStatementCache::checkinStatement:forQuery: for reclaimation. 165 | */ 166 | - (sqlite3_stmt *) checkoutStatementForQueryString: (NSString *) query { 167 | sqlite3_stmt *stmt; 168 | 169 | OSSpinLockLock(&_lock); { 170 | /* Fetch the statement set for this query */ 171 | CFMutableArrayRef stmtArray = (CFMutableArrayRef) [_availableStatements objectForKey: query]; 172 | if (stmtArray == nil || CFArrayGetCount(stmtArray) == 0) { 173 | OSSpinLockUnlock(&_lock); 174 | return NULL; 175 | } 176 | 177 | /* Pop the statement from the array */ 178 | stmt = (sqlite3_stmt *) CFArrayGetValueAtIndex(stmtArray, 0); 179 | CFArrayRemoveValueAtIndex(stmtArray, 0); 180 | 181 | /* Decrement the count */ 182 | _size--; 183 | }; OSSpinLockUnlock(&_lock); 184 | 185 | return stmt; 186 | } 187 | 188 | /** 189 | * Remove all unused statements from the cache. This will not invalidate active, in-use statements. 190 | */ 191 | - (void) removeAllStatements { 192 | [self removeAllStatementsHasLock: NO]; 193 | } 194 | 195 | /* Function to be applied to a CF container. Calls sqlite3_finalize() on the supplied value. */ 196 | static void apply_cache_statement_finalize (const void *value, void *context) { 197 | /* Finalize the statement */ 198 | sqlite3_stmt *stmt = (sqlite3_stmt *) value; 199 | sqlite3_finalize(stmt); 200 | } 201 | 202 | /** 203 | * Close the cache, invalidating all registered statements from the cache. 204 | * 205 | * @warning This will remove statements that may be in active use by PLSqlitePreparedStatement instances, and must 206 | * only be called by the PLSqliteDatabase prior to finalization. 207 | */ 208 | - (void) close { 209 | OSSpinLockLock(&_lock); { 210 | 211 | /* Finalize all registered statements */ 212 | if (_allStatements != NULL) { 213 | CFSetApplyFunction(_allStatements, apply_cache_statement_finalize, NULL); 214 | CFSetRemoveAllValues(_allStatements); 215 | } 216 | 217 | /* Empty the statement cache of the now invalid references. */ 218 | [_availableStatements removeAllObjects]; 219 | } OSSpinLockUnlock(&_lock); 220 | } 221 | 222 | @end 223 | 224 | @implementation PLSqliteStatementCache (PrivateMethods) 225 | 226 | /* Function to be applied to a CF container. Calls sqlite3_finalize() on the supplied value, and removes it from _allStatements. */ 227 | static void apply_cache_remove_statement (const void *value, void *context) { 228 | PLSqliteStatementCache *self = context; 229 | 230 | /* Finalize the statement */ 231 | sqlite3_stmt *stmt = (sqlite3_stmt *) value; 232 | sqlite3_finalize(stmt); 233 | 234 | /* Clean up remaining statement reference */ 235 | CFSetRemoveValue(self->_allStatements, stmt); 236 | } 237 | 238 | /** 239 | * Remove all unused statements from the cache. This will not invalidate active, in-use statements. 240 | * 241 | * @param locked If NO, the implementation will acquire a lock on _lock; otherwise, 242 | * it is assumed that _lock is already held. 243 | */ 244 | - (void) removeAllStatementsHasLock: (BOOL) locked { 245 | if (!locked) 246 | OSSpinLockLock(&_lock); 247 | 248 | /* Iterate over all cached queries and finalize their sqlite statements */ 249 | [_availableStatements enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) { 250 | CFMutableArrayRef array = (CFMutableArrayRef) obj; 251 | CFIndex count = CFArrayGetCount(array); 252 | 253 | /* Finalize all statements */ 254 | CFArrayApplyFunction(array, CFRangeMake(0, count), apply_cache_remove_statement, self); 255 | }]; 256 | 257 | /* Empty the statement cache of the now invalid references. */ 258 | [_availableStatements removeAllObjects]; 259 | 260 | if (!locked) 261 | OSSpinLockUnlock(&_lock); 262 | } 263 | 264 | @end -------------------------------------------------------------------------------- /Classes/PLSqliteStatementCacheTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | #import "PLSqliteDatabase.h" 33 | #import "PLSqliteStatementCache.h" 34 | 35 | @interface PLSqliteStatementCacheTests : SenTestCase { 36 | @private 37 | } 38 | 39 | @end 40 | 41 | /** 42 | * PLSqliteStatementCache Tests 43 | */ 44 | @implementation PLSqliteStatementCacheTests 45 | 46 | /** 47 | * Test basic caching. 48 | */ 49 | - (void) testCaching { 50 | NSError *error; 51 | 52 | /* Create a testing database */ 53 | PLSqliteDatabase *db = [PLSqliteDatabase databaseWithPath: @":memory:"]; 54 | STAssertTrue([db openAndReturnError: &error], @"Database could not be opened: %@", error); 55 | sqlite3 *sqlite = [db sqliteHandle]; 56 | 57 | /* Prepare a statement to test with */ 58 | NSString *queryString = @"SELECT 1"; 59 | sqlite3_stmt *stmt; 60 | const char *unused; 61 | int ret; 62 | 63 | ret = sqlite3_prepare_v2(sqlite, [queryString UTF8String], -1, &stmt, &unused); 64 | STAssertEquals(ret, SQLITE_OK, @"Failed to prepare the statement"); 65 | 66 | /* Try caching it */ 67 | PLSqliteStatementCache *cache = [[[PLSqliteStatementCache alloc] initWithCapacity: 500] autorelease]; 68 | [cache registerStatement: stmt]; 69 | [cache checkinStatement: stmt forQuery: queryString]; 70 | 71 | /* Make sure we can get it back out again */ 72 | sqlite3_stmt *cached_stmt = [cache checkoutStatementForQueryString: queryString]; 73 | STAssertEquals(stmt, cached_stmt, @"Statement was not cached"); 74 | 75 | /* Re-add it to the cache so that we can test the cache's finalization of statements. */ 76 | [cache checkinStatement: stmt forQuery: queryString]; 77 | 78 | /* Finalize all statements */ 79 | [cache close]; 80 | 81 | /* Now verify that the databases closes cleanly. If it doesn't, that means that the statements were leaked, and an 82 | * exception will be raised. */ 83 | [db close]; 84 | } 85 | 86 | /** 87 | * Test dropping of all statements 88 | */ 89 | - (void) testRemoveAllStatements { 90 | NSError *error; 91 | 92 | /* Create a testing database */ 93 | PLSqliteDatabase *db = [PLSqliteDatabase databaseWithPath: @":memory:"]; 94 | STAssertTrue([db openAndReturnError: &error], @"Database could not be opened: %@", error); 95 | sqlite3 *sqlite = [db sqliteHandle]; 96 | 97 | /* Cache a statement for testing. */ 98 | NSString *queryString = @"SELECT 1"; 99 | sqlite3_stmt *stmt; 100 | const char *unused; 101 | int ret; 102 | 103 | ret = sqlite3_prepare_v2(sqlite, [queryString UTF8String], -1, &stmt, &unused); 104 | STAssertEquals(ret, SQLITE_OK, @"Failed to prepare the statement"); 105 | 106 | PLSqliteStatementCache *cache = [[[PLSqliteStatementCache alloc] initWithCapacity: 500] autorelease]; 107 | [cache registerStatement: stmt]; 108 | [cache checkinStatement: stmt forQuery: queryString]; 109 | 110 | /* Try removing all statements */ 111 | [cache removeAllStatements]; 112 | 113 | /* Verify that fetching the statement fails */ 114 | sqlite3_stmt *cached_stmt = [cache checkoutStatementForQueryString: queryString]; 115 | STAssertTrue(cached_stmt == NULL, @"Cache was not purged"); 116 | 117 | /* Verify that the cache closes cleanly. */ 118 | [cache close]; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /Classes/PLSqliteUnlockNotify.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | #import 32 | 33 | #ifdef PL_DB_PRIVATE 34 | int pl_sqlite3_blocking_prepare_v2 (sqlite3 *db, const char *zSql, int nSql, sqlite3_stmt **ppStmt, const char **pz); 35 | int pl_sqlite3_blocking_step (sqlite3_stmt *pStmt); 36 | #endif /* PL_DB_PRIVATE */ -------------------------------------------------------------------------------- /Classes/PLSqliteUnlockNotify.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * This code is directly derived from the public domain example code available from: 6 | * http://www.sqlite.org/unlock_notify.html 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the copyright holder nor the names of any contributors 17 | * may be used to endorse or promote products derived from this 18 | * software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #import "PLSqliteUnlockNotify.h" 34 | 35 | #import 36 | 37 | /* 38 | * A pointer to an instance of this structure is passed as the user-context 39 | * pointer when registering for an unlock-notify callback. 40 | */ 41 | typedef struct UnlockNotification UnlockNotification; 42 | struct UnlockNotification { 43 | int fired; /* True after unlock event has occured */ 44 | pthread_cond_t cond; /* Condition variable to wait on */ 45 | pthread_mutex_t mutex; /* Mutex to protect structure */ 46 | }; 47 | 48 | /* 49 | * This function is an unlock-notify callback registered with SQLite. 50 | */ 51 | static void unlock_notify_cb(void **apArg, int nArg){ 52 | int i; 53 | for(i=0; imutex); 56 | p->fired = 1; 57 | pthread_cond_signal(&p->cond); 58 | pthread_mutex_unlock(&p->mutex); 59 | } 60 | } 61 | 62 | /* 63 | * This function assumes that an SQLite API call (either sqlite3_prepare_v2() 64 | * or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the 65 | * associated database connection. 66 | * 67 | * This function calls sqlite3_unlock_notify() to register for an 68 | * unlock-notify callback, then blocks until that callback is delivered 69 | * and returns SQLITE_OK. The caller should then retry the failed operation. 70 | * 71 | * Or, if sqlite3_unlock_notify() indicates that to block would deadlock 72 | * the system, then this function returns SQLITE_LOCKED immediately. In 73 | * this case the caller should not retry the operation and should roll 74 | * back the current transaction (if any). 75 | */ 76 | static int wait_for_unlock_notify(sqlite3 *db){ 77 | int rc; 78 | UnlockNotification un; 79 | 80 | /* Initialize the UnlockNotification structure. */ 81 | un.fired = 0; 82 | pthread_mutex_init(&un.mutex, 0); 83 | pthread_cond_init(&un.cond, 0); 84 | 85 | /* Register for an unlock-notify callback. */ 86 | rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un); 87 | assert( rc==SQLITE_LOCKED || rc==SQLITE_OK ); 88 | 89 | /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 90 | ** or SQLITE_OK. 91 | ** 92 | ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this 93 | ** case this function needs to return SQLITE_LOCKED to the caller so 94 | ** that the current transaction can be rolled back. Otherwise, block 95 | ** until the unlock-notify callback is invoked, then return SQLITE_OK. 96 | */ 97 | if( rc==SQLITE_OK ){ 98 | pthread_mutex_lock(&un.mutex); 99 | if( !un.fired ){ 100 | pthread_cond_wait(&un.cond, &un.mutex); 101 | } 102 | pthread_mutex_unlock(&un.mutex); 103 | } 104 | 105 | /* Destroy the mutex and condition variables. */ 106 | pthread_cond_destroy(&un.cond); 107 | pthread_mutex_destroy(&un.mutex); 108 | 109 | return rc; 110 | } 111 | 112 | /* 113 | ** This function is a wrapper around the SQLite function sqlite3_step(). 114 | ** It functions in the same way as step(), except that if a required 115 | ** shared-cache lock cannot be obtained, this function may block waiting for 116 | ** the lock to become available. In this scenario the normal API step() 117 | ** function always returns SQLITE_LOCKED. 118 | ** 119 | ** If this function returns SQLITE_LOCKED, the caller should rollback 120 | ** the current transaction (if any) and try again later. Otherwise, the 121 | ** system may become deadlocked. 122 | */ 123 | int pl_sqlite3_blocking_step(sqlite3_stmt *pStmt){ 124 | int rc; 125 | while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){ 126 | rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt)); 127 | if( rc!=SQLITE_OK ) break; 128 | sqlite3_reset(pStmt); 129 | } 130 | return rc; 131 | } 132 | 133 | /* 134 | ** This function is a wrapper around the SQLite function sqlite3_prepare_v2(). 135 | ** It functions in the same way as prepare_v2(), except that if a required 136 | ** shared-cache lock cannot be obtained, this function may block waiting for 137 | ** the lock to become available. In this scenario the normal API prepare_v2() 138 | ** function always returns SQLITE_LOCKED. 139 | ** 140 | ** If this function returns SQLITE_LOCKED, the caller should rollback 141 | ** the current transaction (if any) and try again later. Otherwise, the 142 | ** system may become deadlocked. 143 | */ 144 | int pl_sqlite3_blocking_prepare_v2( 145 | sqlite3 *db, /* Database handle. */ 146 | const char *zSql, /* UTF-8 encoded SQL statement. */ 147 | int nSql, /* Length of zSql in bytes. */ 148 | sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ 149 | const char **pz /* OUT: End of parsed string */ 150 | ){ 151 | int rc; 152 | while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){ 153 | rc = wait_for_unlock_notify(db); 154 | if( rc!=SQLITE_OK ) break; 155 | } 156 | return rc; 157 | } -------------------------------------------------------------------------------- /Classes/PlausibleDatabase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * @defgroup functions Plausible Database Functions Reference 32 | */ 33 | 34 | /** 35 | * @defgroup constants Plausible Database Constants Reference 36 | */ 37 | 38 | /** 39 | * @defgroup enums Enumerations 40 | * @ingroup constants 41 | */ 42 | 43 | /** 44 | * @defgroup globals Global Variables 45 | * @ingroup constants 46 | */ 47 | 48 | /** 49 | * @defgroup exceptions Exceptions 50 | * @ingroup constants 51 | */ 52 | 53 | /* Library Includes */ 54 | #import "PLDatabaseConstants.h" 55 | #import "PLResultSet.h" 56 | #import "PLPreparedStatement.h" 57 | #import "PLDatabase.h" 58 | 59 | #import "PLSqliteDatabase.h" 60 | #import "PLSqliteStatementCache.h" 61 | #import "PLSqlitePreparedStatement.h" 62 | #import "PLSqliteResultSet.h" 63 | 64 | #import "PLDatabaseConnectionProvider.h" 65 | 66 | #import "PLDatabaseFilterConnectionProvider.h" 67 | #import "PLDatabasePoolConnectionProvider.h" 68 | #import "PLDatabaseMigrationConnectionProvider.h" 69 | 70 | #import "PLSqliteConnectionProvider.h" 71 | 72 | #import "PLDatabaseMigrationVersionManager.h" 73 | #import "PLDatabaseMigrationTransactionManager.h" 74 | #import "PLDatabaseMigrationManager.h" 75 | #import "PLDatabaseMigrationDelegate.h" 76 | 77 | #import "PLSqliteMigrationManager.h" 78 | 79 | /** 80 | * @mainpage Plausible Database 81 | * 82 | * @section intro_sec Introduction 83 | * 84 | * Plausible Database provides a generic Objective-C interface for interacting with 85 | * SQL databases. SQLite is the initial and primary target, but the API has been 86 | * designed to support more traditional databases. 87 | * 88 | * Plausible Database provides an Objective-C veneer over the underlying SQL database. Objects 89 | * are automatically bound to statement parameters, and converted to and from the underlying SQL datatypes. 90 | * 91 | * Library classes supporting subclassing are explicitly documented. Due to Objective-C's fragile base classes, 92 | * binary compatibility with subclasses is NOT guaranteed. You should avoid subclassing library 93 | * classes -- use class composition instead. 94 | * 95 | * 96 | * @section doc_sections Documentation Sections 97 | * - @subpage exec_sql 98 | * - @subpage error_handling 99 | * 100 | * 101 | * @section services Integration & Development Services 102 | * Plausible Database is provided free of charge under the BSD license, and may be freely integrated with any application. 103 | * We can provide assistance with integrating our code in your own iPhone or Mac application, as well as development of additional features -- 104 | * including support for additional databases -- under a license of your choosing (higher rates apply for non BSD-licensed work). 105 | * Contact Plausible Labs for more information: http://www.plausiblelabs.com 106 | */ 107 | 108 | /** 109 | * @page exec_sql Basic SQL Programming Guide 110 | * 111 | * @section create_conn Creating a Connection 112 | * 113 | * Open a connection to a database file: 114 | * 115 | *
116 |  * PLSqliteDatabase *db = [[PLSqliteDatabase alloc] initWithPath:  @"/path/to/database"];
117 |  * if (![db open])
118 |  *     NSLog(@"Could not open database");
119 |  * 
120 | * 121 | * @section exec_update Update Statements 122 | * 123 | * Update statements can be executed using -[PLDatabase executeUpdate:] 124 | * 125 | *
126 |  * if (![db executeUpdate: @"CREATE TABLE example (id INTEGER)"])
127 |  *     NSLog(@"Table creation failed");
128 |  *
129 |  * if (![db executeUpdate: @"INSERT INTO example (id) VALUES (?)", [NSNumber numberWithInteger: 42]])
130 |  *     NSLog(@"Data insert failed");
131 |  * 
132 | * @section exec_query Query Statements 133 | * 134 | * Queries can be executed using -[PLDatabase executeQuery:]. To iterate over the returned results, an NSObject instance 135 | * conforming to PLResultSet will be returned. 136 | * 137 | *
138 |  * id results = [db executeQuery: @"SELECT id FROM example WHERE id = ?", [NSNumber numberWithInteger: 42]];
139 |  * while ([results next]) {
140 |  *     NSLog(@"Value of column id is %d", [results intForColumn: @"id"]);
141 |  * }
142 |  *
143 |  * // Failure to close the result set will not leak memory, but may
144 |  * // retain database resources until the instance is deallocated.
145 |  * [results close];
146 |  * 
147 | * 148 | * @section prepared_stmt Prepared Statements 149 | * 150 | * Pre-compilation of SQL statements and advanced parameter binding 151 | * are supported by PLPreparedStatement. A prepared statement can 152 | * be constructed using -[PLDatabase prepareStatement:]. 153 | * 154 | *
155 |  * id stmt = [db prepareStatement: @"INSERT INTO example (name, color) VALUES (?, ?)"];
156 |  
157 |  * // Bind the parameters
158 |  * [stmt bindParameters: [NSArray arrayWithObjects: @"Widget", @"Blue", nil]];
159 |  *
160 |  * // Execute the INSERT
161 |  * if ([stmt executeUpdate] == NO)
162 |  *     NSLog(@"INSERT failed");
163 |  *
164 |  * 
165 | * 166 | * @subsection named_params Name-based Parameter Binding 167 | * 168 | * Name-based parameter binding is also supported: 169 | * 170 | *
171 |  * // Prepare the statement
172 |  * id stmt = [db prepareStatement: @"INSERT INTO test (name, color) VALUES (:name, :color)"];
173 |  *
174 |  * // Bind the parameters using a dictionary
175 |  * NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity: 2];
176 |  * [parameters setObject: @"Widget" forKey: @"name"];
177 |  * [parameters setObject: @"Blue" forKey: @"color"];
178 |  *
179 |  * [stmt bindParameterDictionary: parameters];
180 |  *
181 |  * // Execute the INSERT
182 |  * if ([stmt executeUpdate] == NO)
183 |  *     NSLog(@"INSERT failed");
184 |  *
185 |  * 
186 | */ 187 | 188 | /** 189 | * @page error_handling Error Handling Programming Guide 190 | * 191 | * Where a method may return an error, Plausible Database provides access to the underlying cause via an optional NSError argument. 192 | * 193 | * All returned errors will be a member of one of the below defined domains, however, new domains and error codes may be added at any time. 194 | * If you do not wish to report on the error cause, many methods support a simple form that requires no NSError argument. 195 | * 196 | * @section Error Domains, Codes, and User Info 197 | * 198 | * @subsection database_errors Database Errors 199 | * 200 | * Any errors in the database driver use the #PLDatabaseErrorDomain error domain, and and one of the error codes defined in #PLDatabaseError. Additionally, the 201 | * following keys will be available in the NSError user info dictionary: 202 | * 203 | * - #PLDatabaseErrorQueryStringKey - Query which caused the error (optional). 204 | * - #PLDatabaseErrorVendorErrorKey - The native database error code. 205 | * - #PLDatabaseErrorVendorStringKey - The native database error string. 206 | */ 207 | -------------------------------------------------------------------------------- /Classes/PlausibleDatabase.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "PlausibleDatabase.h" 31 | #import "PLDatabaseConstants.h" 32 | 33 | /** 34 | * Generic Database Exception 35 | * @ingroup exceptions 36 | */ 37 | NSString *PLDatabaseException = @"PLDatabaseException"; 38 | 39 | /** Plausible Database NSError Domain 40 | * @ingroup globals */ 41 | NSString *PLDatabaseErrorDomain = @"PLDatabaseErrorDomain"; 42 | 43 | /** 44 | * Key to retrieve the optionally provided SQL query which caused the error from an NSError in the PLDatabaseErrorDomain, as an NSString 45 | * @ingroup globals 46 | */ 47 | NSString *PLDatabaseErrorQueryStringKey = @"PLDatabaseErrorQueryStringKey"; 48 | 49 | /** 50 | * Key to retrieve the native database error code from an NSError in the PLDatabaseErrorDomain, as an NSNumber 51 | * @ingroup globals 52 | */ 53 | NSString *PLDatabaseErrorVendorErrorKey = @"PLDatabaseErrorVendorErrorKey"; 54 | 55 | /** 56 | * Key to retrieve the native database error string from an NSError in the PLDatabaseErrorDomain, as an NSString 57 | * @ingroup globals 58 | */ 59 | NSString *PLDatabaseErrorVendorStringKey = @"PLDatabaseErrorVendorStringKey"; 60 | 61 | /** 62 | * @internal 63 | * Implementation-private utility methods. 64 | */ 65 | @implementation PlausibleDatabase 66 | 67 | /** 68 | * @internal 69 | * 70 | * Create a new NSError in the PLDatabaseErrorDomain. 71 | * 72 | * @param errorCode The error code. 73 | * @param localizedDescription A localized error description. 74 | * @param queryString The optional query which caused the error. 75 | * @param nativeCode The native SQL driver's error code. 76 | * @param nativeString The native SQL driver's non-localized error string. 77 | * @return A NSError that may be returned to the API caller. 78 | */ 79 | + (NSError *) errorWithCode: (PLDatabaseError) errorCode localizedDescription: (NSString *) localizedDescription 80 | queryString: (NSString *) queryString vendorError: (NSNumber *) vendorError 81 | vendorErrorString: (NSString *) vendorErrorString 82 | { 83 | NSMutableDictionary *userInfo; 84 | 85 | /* Create the userInfo dictionary */ 86 | userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: 87 | localizedDescription, NSLocalizedDescriptionKey, 88 | vendorError, PLDatabaseErrorVendorErrorKey, 89 | vendorErrorString, PLDatabaseErrorVendorStringKey, 90 | nil]; 91 | 92 | /* Optionally insert the query string. */ 93 | if (queryString != nil) 94 | [userInfo setObject: queryString forKey: PLDatabaseErrorQueryStringKey]; 95 | 96 | /* Return the NSError */ 97 | return [NSError errorWithDomain: PLDatabaseErrorDomain code: errorCode userInfo: userInfo]; 98 | } 99 | 100 | @end -------------------------------------------------------------------------------- /Classes/PlausibleDatabaseTests.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #import 32 | 33 | #import 34 | 35 | @interface PlausibleDatabaseTests : SenTestCase { 36 | @private 37 | } 38 | 39 | @end 40 | 41 | @implementation PlausibleDatabaseTests 42 | 43 | /* Test NSError creation */ 44 | - (void) testDatabaseError { 45 | NSError *error = [PlausibleDatabase errorWithCode: PLDatabaseErrorFileNotFound 46 | localizedDescription: @"test" 47 | queryString: @"query" 48 | vendorError: [NSNumber numberWithInt: 42] 49 | vendorErrorString: @"native"]; 50 | 51 | STAssertTrue([PLDatabaseErrorDomain isEqual: [error domain]], @"Domain incorrect"); 52 | STAssertEquals(PLDatabaseErrorFileNotFound, (PLDatabaseError) [error code], @"Code incorrect"); 53 | STAssertTrue([@"test" isEqual: [error localizedDescription]], @"Description incorrect (Wanted: test, Got: \"%@""", [error localizedDescription]); 54 | 55 | STAssertTrue([@"query" isEqual: [[error userInfo] objectForKey: PLDatabaseErrorQueryStringKey]], @"Query string incorrect"); 56 | 57 | STAssertEquals(42, [[[error userInfo] objectForKey: PLDatabaseErrorVendorErrorKey] intValue], @"Native error code incorrect"); 58 | STAssertTrue([@"native" isEqual: [[error userInfo] objectForKey: PLDatabaseErrorVendorStringKey]], @"Native error string incorrect"); 59 | } 60 | 61 | @end -------------------------------------------------------------------------------- /Dependencies/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains source and binary dependencies: 2 | 3 | SQLite 4 | Description: 5 | An up-to-date embeddedable version of SQLite. Mac OS X and iOS ship SQLite, but it is 6 | often out-of-date, patched, and often built with unexpected or limited options. 7 | 8 | Version: 9 | 3.7.13 amalgamation downloaded from http://www.sqlite.org/download.html 10 | 11 | License: 12 | Public Domain 13 | 14 | Modifications: 15 | The library has not been modified, and built version of the library has been included in the repository. 16 | To rebuild libsqlite3.a, execute the following from within the SQLite directory: 17 | make clean && make -j all && make clean-objs 18 | -------------------------------------------------------------------------------- /Dependencies/SQLite/Makefile: -------------------------------------------------------------------------------- 1 | DEVELOPER?= $(shell xcode-select -print-path) 2 | PLATFORMS?= $(DEVELOPER)/Platforms/ 3 | CFLAGS?= -Os -std=c99 \ 4 | -DSQLITE_ENABLE_UNLOCK_NOTIFY 5 | 6 | DEVICE_SDK?= iPhoneOS5.1 7 | DEVICE_PLATFORM?= $(PLATFORMS)/iPhoneOS.platform 8 | DEVICE_ROOT?= $(DEVICE_PLATFORM)/Developer/SDKs/$(DEVICE_SDK).sdk 9 | DEVICE_GCC?= $(shell xcrun -sdk `echo ${DEVICE_SDK} | tr [A-Z] [a-z]` -f clang) 10 | DEVICE_CFLAGS?= -isysroot "$(DEVICE_ROOT)" -gdwarf-2 -miphoneos-version-min=3.0 11 | DEVICE_CFLAGS_ARMV6?= -arch armv6 -mthumb $(DEVICE_CFLAGS) 12 | DEVICE_CFLAGS_ARMV7?= -arch armv7 $(DEVICE_CFLAGS) 13 | 14 | 15 | SIM_SDK?= iPhoneSimulator5.1 16 | SIM_PLATFORM?= $(PLATFORMS)/iPhoneSimulator.platform 17 | SIM_ROOT?= $(SIM_PLATFORM)/Developer/SDKs/$(SIM_SDK).sdk 18 | SIM_GCC?= $(shell xcrun -sdk `echo ${SIM_SDK} | tr '[A-Z]' '[a-z]'` -f clang) 19 | SIM_CFLAGS?= -arch i386 -isysroot "$(SIM_ROOT)" -mmacosx-version-min=10.6 -gdwarf-2 20 | 21 | MAC_SDK?= MacOSX10.7 22 | MAC_PLATFORM?= $(PLATFORMS)/MacOSX.platform 23 | MAC_ROOT?= $(MAC_PLATFORM)/Developer/SDKs/$(MAC_SDK).sdk 24 | MAC_GCC?= $(shell xcrun -sdk `echo ${MAC_SDK} | tr '[A-Z]' '[a-z]'` -f clang) 25 | MAC_CFLAGS?= -arch x86_64 -arch i386 -isysroot "$(MAC_ROOT)" -mmacosx-version-min=10.6 -gdwarf-2 26 | 27 | IOS_OBJS= sqlite3-sim.o \ 28 | sqlite3-ios-armv6.o \ 29 | sqlite3-ios-armv7.o 30 | 31 | MAC_OBJS= sqlite3-macosx.o 32 | 33 | PRODUCTS= $(IOS_PRODUCT) $(MAC_PRODUCT) 34 | IOS_PRODUCT= libplsqlite3-ios.a 35 | MAC_PRODUCT= libplsqlite3-macosx.a 36 | 37 | all: $(PRODUCTS) 38 | 39 | sqlite3-sim.o: sqlite3.c 40 | $(SIM_GCC) $(CFLAGS) $(SIM_CFLAGS) -c $< -o $@ 41 | 42 | sqlite3-ios-armv6.o: sqlite3.c 43 | $(DEVICE_GCC) $(CFLAGS) $(DEVICE_CFLAGS_ARMV6) -c $< -o $@ 44 | 45 | sqlite3-ios-armv7.o: sqlite3.c 46 | $(DEVICE_GCC) $(CFLAGS) $(DEVICE_CFLAGS_ARMV7) -c $< -o $@ 47 | 48 | sqlite3-macosx.o: sqlite3.c 49 | $(MAC_GCC) $(CFLAGS) $(MAC_CFLAGS) -c $< -o $@ 50 | 51 | $(MAC_PRODUCT): $(MAC_OBJS) 52 | /usr/bin/libtool -static $+ -o $@ 53 | 54 | $(IOS_PRODUCT): $(IOS_OBJS) 55 | /usr/bin/libtool -static $+ -o $@ 56 | 57 | clean-objs: 58 | rm -f $(IOS_OBJS) $(MAC_OBJS) 59 | 60 | clean: clean-objs 61 | rm -f $(PRODUCTS) 62 | -------------------------------------------------------------------------------- /Dependencies/SQLite/libplsqlite3-ios.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/pldatabase/e91683f23b87512ca84424111ffac371bf9d07ab/Dependencies/SQLite/libplsqlite3-ios.a -------------------------------------------------------------------------------- /Dependencies/SQLite/libplsqlite3-macosx.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/pldatabase/e91683f23b87512ca84424111ffac371bf9d07ab/Dependencies/SQLite/libplsqlite3-macosx.a -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Plausible Labs Cooperative, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the copyright holders nor the names of any 13 | contributors may be used to endorse or promote products derived 14 | from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Other Sources/Prefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2011 Plausible Labs Cooperative, Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the copyright holder nor the names of any contributors 14 | * may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | -------------------------------------------------------------------------------- /PlausibleDatabase.xcodeproj/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plausiblelabs/pldatabase/e91683f23b87512ca84424111ffac371bf9d07ab/PlausibleDatabase.xcodeproj/TemplateIcon.icns -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLDatabase 2 | 3 | PLDatabase provides an SQL database access library for Objective-C, focused on SQLite as an application database. The library supports both macOS and iOS development. 4 | 5 | ## Basic Usage 6 | 7 | ### Creating a Connection 8 | 9 | Open a connection to a database file: 10 | 11 | ```objectivec 12 | PLSqliteDatabase *db = [[PLSqliteDatabase alloc] initWithPath: @"/path/to/database"]; 13 | if (![db openAndReturnError: &error]) { 14 | NSLog(@"Could not open database"); 15 | } 16 | ``` 17 | 18 | ### Update Statements 19 | 20 | Update statements can be executed directly via `-[PLDatabase executeUpdateAndReturnError:statement:...]`: 21 | 22 | ```objectivec 23 | if (![db executeUpdateAndReturnError: &error statement: @"CREATE TABLE example (id INTEGER)"]) { 24 | NSLog(@"Table creation failed"); 25 | } 26 | 27 | if (![db executeQueryAndReturnError: &error statement: @"INSERT INTO example (id) VALUES (?)", [NSNumber numberWithInteger: 42]]) { 28 | NSLog(@"Data insert failed"); 29 | } 30 | ``` 31 | 32 | ### Query Statements 33 | 34 | Queries can be executed using `-[PLDatabase executeQueryAndReturnError:statement:...]`. To iterate over the returned results, a instance conforming to the `PLResultSet` protocol will be returned: 35 | 36 | ```objectivec 37 | id results = [db executeQueryAndReturnError: &error statement: @"SELECT id FROM example WHERE id = ?", [NSNumber numberWithInteger: 42]]; 38 | PLResultSetStatus rss; 39 | while ((rss = [results nextAndReturnError: &error]) == PLResultSetStatusRow) { 40 | NSLog(@"Value of column id is %d", [results intForColumn: @"id"]); 41 | } 42 | 43 | if (rss != PLResultSetStatusDone) { 44 | NSLog(@"Iterating results failed"); 45 | } 46 | 47 | // Failure to close the result set will not leak memory, but may 48 | // retain database resources until the instance is deallocated. 49 | [results close]; 50 | ``` 51 | 52 | ### Prepared Statements 53 | 54 | Pre-compilation of SQL statements and advanced parameter binding are supported by `PLPreparedStatement`. A prepared statement can be constructed using `-[PLDatabase prepareStatement:error:]`. 55 | 56 | ```objectivec 57 | id stmt = [db prepareStatement: @"INSERT INTO example (name, color) VALUES (?, ?)" error: &error]; 58 | 59 | // Bind the parameters 60 | [stmt bindParameters: @["Widget", @"Blue"]]; 61 | 62 | // Execute the INSERT 63 | if ([stmt executeUpdateAndReturnError: &error] == NO) { 64 | NSLog(@"INSERT failed"); 65 | } 66 | ``` 67 | 68 | ### Name-based Parameter Binding 69 | 70 | Name-based parameter binding is also supported: 71 | 72 | ```objectivec 73 | // Prepare the statement 74 | id stmt = [db prepareStatement: @"INSERT INTO test (name, color) VALUES (:name, :color)" error: &error]; 75 | 76 | // Bind the parameters using a dictionary 77 | [stmt bindParameterDictionary: @{ @"name" : @"Widget", @"color" : @"Blue" }]; 78 | 79 | // Execute the INSERT 80 | if ([stmt executeUpdateAndReturnError: &error] == NO) { 81 | NSLog(@"INSERT failed"); 82 | } 83 | ``` 84 | 85 | ## Building 86 | 87 | To build your own release binary, build the 'Disk Image' target: 88 | ``` 89 | $ xcodebuild -configuration Release -target 'Disk Image' 90 | ``` 91 | 92 | This will output a new release disk image containing an embeddable macOS framework and a static iOS framework in `build/Release/Plausible Database-{version}.dmg`. 93 | 94 | ## License 95 | 96 | PLDatabase is provided free of charge under the BSD license, and may be freely integrated with any application. See the LICENSE file for the full license. 97 | -------------------------------------------------------------------------------- /Resources/PlausibleDatabase-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.plausiblelabs.${PRODUCT_NAME:identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | FMWK 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.${PRODUCT_NAME:identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | --------------------------------------------------------------------------------