SecureDatabaseStore
public class SecureDatabaseStore
SecureDatabaseStore
The Secure Storage component provides transparent 256-bit AES encryption of data stored either in a SQLite database or a persistent key-value store. See https://www.sqlite.org/docs.html for more information about SQLite.
Note: This component is only using encryption mechanisms which are available in iOS.
Usage
There are four main classes in the SecureStorage module:
SecureDatabaseStore
- represents a single SQLite database, used for executing SQL statements. TheSecureDatabaseStore
is not thread safe. For thread safety seeSecureDatabaseQueue
and the Thread Safety section.SecureDatabaseQueue
- use this class to perform queries and updates on multiple threads. See Thread Safety for details.SecureDatabaseResultSet
- represents the results of executing a query on aSecureDatabaseStore
.SecureKeyValueStore
- represents a key-value store, used for storing data in a non-relational way.
Store Creation
Every store (either a SecureDatabaseStore
, a SecureDatabaseQueue
or a SecureKeyValueStore
) is created with its constructor.
A database or a key-value store type can be created with one of the following three options:
- A default location for the database file. No parameters need to be passed to the constructor.
- A
databaseFileName: String
parameter that specifies the file name and the file extension of the database file. - A
fullDatabasePath: String
parameter that specifies the full file path of the database.
The SecureKeyValueStore
is backed by a SQLite database, which is why its creation methods can be called with a file name and a directory.
A SecureDatabaseQueue
is a wrapper around a SecureDatabaseStore
and must be initialized with a database store object.
Setting an Encryption Key
After initializing SecureDatabaseStore
or SecureKeyValueStore
an initial encryption key is not set. To set the encryption key of a store, call the open(encryptionKey: String)
method. This sets the encryption key when the store is opened for the first time and when data is written afterwards.
Database
Creating
// for example if you want to store your file in the document directory:
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fullPath = fileURL.appendingPathComponent("myDatabase.db").path
//or let fullPath: String = "<#/path/to/database/directory/database.db#>"
let db = SecureDatabaseStore(fullDatabasePath: fullPath)
Opening
You must open the store before you can interact with it. The store fails to open if there are insufficient resources or permissions to open and/or create the store. The store can also fail to open if the encryption key is an empty string or the wrong key is used. When the store is opened for the first time, the provided key is set to that of the database’s encryption key:
do {
try db.open(with: "encryptionKey")
} catch let error {
// log the error using Logger
logger.error("An error occurred while opening the database.", error: error)
}
Executing Updates
Any SQL statement that is not a SELECT
statement qualifies as an update, including CREATE
, UPDATE
, INSERT
, ALTER
, COMMIT
, BEGIN
, DETACH
, DELETE
, DROP
, END
, EXPLAIN
, VACUUM
, and REPLACE
statements (and many more). Basically, if your SQL statement does not begin with SELECT
, it is an update statement.
Executing updates can throw errors if something fails:
do {
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
try db.executeUpdate("UPDATE myTable SET column=:value", parameterDictionary: [":value" : 2])
} catch let error {
logger.error("An error occurred while executing an update or an insert on the database.", error: error)
}
Executing Queries
A SELECT
statement is a query and executes via one of the executeQuery(...)
methods.
Executing queries returns a SecureDatabaseResultSet
object if successful, and throws an error upon failure.
You typically use a while()
loop to iterate over the results of your query. You also need to “step” from one record to the next. The easiest way to do this is by using this code:
do {
let rs = try db.executeQuery("SELECT * FROM myTable")
defer {
rs.close()
}
while try rs.next() {
//retrieve values for each record
}
} catch let error {
logger.error("An error occurred while executing a query on the database.", error: error)
}
You must always invoke SecureDatabaseResultSet.next()
before attempting to access the values returned in a query, even if you’re only expecting one:
do {
let rs = try db.executeQuery("SELECT COUNT(*) FROM myTable")
defer {
rs.close()
}
if try rs.next() {
let totalCount = rs.int(forColumnIndex: 0)
}
} catch let error {
logger.error("An error occurred while executing a query on the database.", error: error)
}
A SecureDatabaseResultSet
contains many methods for retrieving different data types in an appropriate format:
int(forColumnName columnName: String) -> Int?
int64(forColumnName columnName: String) -> Int64?
uint64(forColumnName columnName: String) -> UInt64?
bool(forColumnName columnName: String) -> Bool?
double(forColumnName columnName: String) -> Double?
string(forColumnName columnName: String) -> String?
date(forColumnName columnName: String) -> Date?
data(forColumnName columnName: String) -> Data?
object<T: SQLiteDatatypeBridgeable>(forColumnName columnName: String) throws -> T?
Each of these methods also has a {type}(forColumnIndex columnIndex: Int) -> {type}?
variant that is used to retrieve the data based on the position of the column in the results, as opposed to the column’s name.
Typically, there’s no need to close()
a SecureDatabaseResultSet
yourself, since that happens when either the result set is deallocated, or the parent database is closed. However, we recommend closing the result set explicitly.
Closing
When you finish executing queries and updates on the database, close()
the SecureDatabaseStore
so that SQLite relinquishes any resources it has acquired during the course of its operation:
db.close()
Multiple Statements and Batch Commands
You can use SecureDatabaseStore
‘s executeStatements(sql:, resultBlock:)
to execute multiple statements in a string:
let sql = "CREATE TABLE bulktest1 (id INTEGER PRIMARY KEY AUTOINCREMENT, x TEXT);" +
"CREATE TABLE bulktest2 (id INTEGER PRIMARY KEY AUTOINCREMENT, y TEXT);" +
"CREATE TABLE bulktest3 (id INTEGER PRIMARY KEY AUTOINCREMENT, z TEXT);" +
"INSERT INTO bulktest1 (x) VALUES ('XXX');" +
"INSERT INTO bulktest2 (y) VALUES ('YYY');" +
"INSERT INTO bulktest3 (z) VALUES ('ZZZ');"
do {
try db.executeStatements(sql)
} catch let error {
logger.error("An error occurred while executing a statements on the database.", error: error)
}
let sql = "SELECT COUNT(*) AS count FROM bulktest1;" +
"SELECT COUNT(*) AS count FROM bulktest2;" +
"SELECT COUNT(*) AS count FROM bulktest3;"
do {
try db.executeStatements(sql) { resultDictionary in
if let count = resultDictionary["count"] {
self.logger.info("bulktest1 contains \(count) rows")
return true
} else {
self.logger.info("Something went wrong. Stop executing all following statements.")
return false
}
}
} catch let error {
logger.error("An error occurred while executing statements on the database.", error: error)
}
Data Sanitization
When providing a SQL statement, use the standard SQLite binding syntax. Use placeholders for values to be inserted, updated or used in WHERE clauses in SELECT statements.
Do not construct the SQL String yourself from variables like this:
let sql = "INSERT INTO myTable VALUES (\(value1), \(value1), \(value1))"
Instead, use the standard SQLite binding syntax like this:
let sql = "INSERT INTO myTable VALUES (?, ?, ?)"
let argsArray = ["MyId", "MyName", "MyValue"]
try db.executeUpdate(sql, values: argsArray)
The ?
character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a representation of arguments in an Array or a Dictionary, which are then properly escaped for you.
Alternatively, you can use named parameter syntax:
let sql = "INSERT INTO myTable VALUES (:id, :name, :value)"
let argsDict = ["id" : "MyId", "name" : "MyName", "value" : "MyValue"]
try db.executeUpdate(sql, parameterDictionary: argsDict)
The parameters must start with a colon. SQLite supports other characters, but internally the Dictionary keys are prefixed with a colon.
Do not include the colon in your dictionary keys.
SQLite does not allow the binding syntax for table, column or schema names.
Shared store
The SecureDatabaseStore
doesn’t support sharing capability. If the shared store is mandatory for a scenario it is recommended to use the KeychainStorage
.
For more information about KeychainStorage
and the details about sharing store see the KeychainStorage documentation.
Thread Safety
It is not safe to use a single instance of a SecureDatabaseStore
from multiple threads. You can make a SecureDatabaseStore
object per thread, but do not share a single instance across threads or across multiple threads at the same time.
Do not instantiate a single SecureDatabaseStore
object and use it across multiple threads directly.
Instead, use a SecureDatabaseQueue
. The SecureDatabaseQueue
class is a wrapper around a SecureDatabaseStore
, which can perform multiple database calls on a serial queue making more calls thread safe at a time. Therefore, it is thread-safe with respect to direct calls on the SecureDatabaseQueue
.
First, make a queue with your SecureDatabaseStore instance:
let queue = SecureDatabaseQueue(databaseStore: db)
Then you can perform operations on the database such as:
do {
try queue.inDatabase { db in
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
let rs = try db.executeQuery("SELECT * from myTable")
defer {
rs.close()
}
while try rs.next() {
//...
}
}
} catch let error {
logger.error("An error occurred while executing a updates on a database queue.", error: error)
}
You can also wrap multiple operations in a transaction like this:
do {
try queue.inExclusiveTransaction { db in
do {
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
} catch {
return true // perform a rollback
}
return false
}
} catch let error {
logger.error("An error occurred while executing a updates on a database queue.", error: error)
}
Key-Value Store
The SecureKeyValueStore
is based on a SQLite database. It can store any values that conform to the NSCoding
protocol.
Its lifecycle is similar to that of a SecureDatabaseStore
(see the Database section above). It provides methods to open and close the store, as well as for changing the encryption key. The encryption key of a SecureKeyValueStore
is initially set when you make the first call to open(enryptionKey: String)
.
A SecureKeyValueStore
has many methods to set and retrieve different data types in an appropriate format:
Put
put<T: NSCoding>(_ value: T?, forKey key: String) throws
put<T: SQLiteDatatypeBridgeable>(_ value: T?, forKey key: String) throws
putAll(_ keyValueDict: Dictionary<String, NSCoding?>) throws
Get
You must specify the type of data to retrieve it from the key value store.
Conversions between different types of numbers may produce erroneous results. Such as putting an Int64.max
for a specific key and getting this value with type: Int8
. Conversions are handled in accordance to the behavior of NSNumber
. See https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/index.html for more information.
get<T: NSCoding>(_ key: String) throws -> T?
get<T: SQLiteDatatypeBridgeable>(_ key: String) throws -> T?
getString(_ key: String) throws -> String?
getInt8(_ key: String) throws -> Int8?
getInt16(_ key: String) throws -> Int16?
getInt32(_ key: String) throws -> Int32?
getInt64(_ key: String) throws -> Int64?
getInt(_ key: String) throws -> Int?
getUInt8(_ key: String) throws -> UInt8?
getUInt16(_ key: String) throws -> UInt16?
getUInt32(_ key: String) throws -> UInt32?
getUInt64(_ key: String) throws -> UInt64?
getUInt(_ key: String) throws -> UInt?
getFloat(_ key: String) throws -> Float?
getDouble(_ key: String) throws -> Double?
getBool(_ key: String) throws -> Bool?
getData(_ key: String) throws -> NSData?
Get with default value
These methods work the same way as the get methods, except that you can specify a defaultValue if no mapping of the desired type exists for the given key or if a nil
value is explicitly associated with the given key:
get<T: NSCoding>(_ key: String, defaultValue: T?) throws -> T?
get<T: SQLiteDatatypeBridgeable>(_ key: String, defaultValue: T?) throws -> T?
getString(_ key: String, defaultValue: String?) throws -> String?
getInt8(_ key: String, defaultValue: Int8?) throws -> Int8?
getInt16(_ key: String, defaultValue: Int16?) throws -> Int16?
getInt32(_ key: String, defaultValue: Int32?) throws -> Int32?
getInt64(_ key: String, defaultValue: Int64?) throws -> Int64?
getInt(_ key: String, defaultValue: Int?) throws -> Int?
getUInt8(_ key: String, defaultValue: UInt8?) throws -> UInt8?
getUInt16(_ key: String, defaultValue: UInt16?) throws -> UInt16?
getUInt32(_ key: String, defaultValue: UInt32?) throws -> UInt32?
getUInt64(_ key: String, defaultValue: UInt64?) throws -> UInt64?
getUInt(_ key: String, defaultValue: UInt?) throws -> UInt?
getFloat(_ key: String, defaultValue: Float?) throws -> Float?
getDouble(_ key: String, defaultValue: Double?) throws -> Double?
getBool(_ key: String, defaultValue: Bool?) throws -> Bool?
getData(_ key: String, defaultValue: NSData?) throws -> NSData?
SecureStorage component Logger ID
This component uses the following name prefix for logging: 'SAP.Foundation.SecureStorage’
SecureDatabaseStore class
Represents a single SQLite database, can be used by executing SQL statements.
The SecureDatabaseStore instance is not thread safe. Using directly from multiple threads can lead to unexpected behaviors. To use it a thread-safe way use the SecureDatabaseQueue.
To initialize a default SecureDatabaseStore
you would use the following lines somewhere central in your application as soon as you have an encryption key for that store.
var secureDatabaseStore = SecureDatabaseStore()
try secureDatabaseStore.open(with: "your_encryption_key")
You can read from and write to the SecureDatabaseStore
instance as long as it is not being closed.
To create the SecureDatabaseStore
at a custom location, please use the following code:
let customDatabasePath = "/your_custom_dir/your_custom_name.db"
var secureDatabaseStore = SecureDatabaseStore(fullDatabasePath: customDatabasePath)
try secureDatabaseStore.open(with: "your_encryption_key")
-
The URL of the database.
Declaration
Swift
public let databaseURL: URL
-
Initialize a new
SecureDatabaseStore
in the default location with the specified or default database file name.Declaration
Swift
public convenience init(databaseFileName: String = "SAPcpSDKDefaultSecureDatabaseStore.db")
Parameters
databaseFileName
a database file name with a default value
-
Initialize a new
SecureDatabaseStore
in the specified path.Declaration
Swift
public convenience init(fullDatabasePath: String)
Parameters
fullDatabasePath
the full path of the database file name
-
Opens the
SecureStore
with the respective encryption key. This call sets the encryption key of a store if the store is opened for the first time and if data is written afterwards. The underlying store is opened for read and write access and it is created at the specified path if it does not already exist.Note
The actual database encryption key is drived from the StringencryptionKey
with PBKDF2.Note
In case the store is already opened the method doesn’t have any effect and it won’t validate the passcode.
Throws
Throws:
SecureStorageError.authenticationFailed(message:)
if an error occured while setting the encryption key and if the encyption key is wrong or an empty string.SecureStorageError.openFailed(code:message:)
if something went wrong while opening and/or creating the store. Error codes correspond to sqlite3 error codes (see https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func open(with encryptionKey: String, initializationHandler: ((SecureDatabaseStore) -> Error?) = SecureDatabaseStore.defaultInitializationHandler) throws
Parameters
encryptionKey
the secure store’s encryption key as String.
initializationHandler
called when the SecureDatabaseStore is created the first time. Not called if the database file exists before the open. Provide your database initialization code here. The closure is called serially.
-
Opens the
SecureStore
with the respective encryption key. The underlying store is opened for read and write access. The store at the specified path is created if it does not exist.Warning
The binary
encryptionKeyData
data is used directly as the database encryption key. It must be exactly 256 bit in length.Throws
Throws:
SecureStorageError.authenticationFailed(message:)
if an error occured while setting the encryption key and if the encyption key is wrong or an empty string.SecureStorageError.openFailed(code:message:)
if something went wrong while opening and/or creating the store. Error codes correspond to sqlite3 error codes if the underlying store is a sqlite database (see https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func open(with encryptionKeyData: Data, initializationHandler: (SecureDatabaseStore) -> Error? = SecureDatabaseStore.defaultInitializationHandler) throws
Parameters
encryptionKeyData
the secure store’s binary encryption key as Data. It must be exactly 256 bit in length.
initializationHandler
called when the SecureDatabaseStore is created the first time. Not called if the database file exists before the open. Provide your database initialization code here. The closure is called serially.
-
Returns the state of the store
Declaration
Swift
public func isOpen() -> Bool
Return Value
true if the store is open and can be used false otherwise
-
Closes the database. No further access is possible.
Declaration
Swift
public func close()
-
Changes the encryption key of the
SecureStore
if it has been opened. OtherwiseSecureStorageError.Closed
is thrown and the encryption key remains unchanged. Changing the key means re-encryption of the whole database which can take a long time on larger databases.Throws
SecureStorageError.Closed
if the store is locked with its encryption key.Throws
SecureStorageError.EncryptionKeyChangeFailed
if an error occured while changing the encryption key.Declaration
Swift
public func changeEncryptionKey(with newEncryptionKey: String) throws
Parameters
newEncryptionKey
the
SecureStore
’s new encryption key as String. -
Changes the encryption key of the
SecureStore
if it has been opened. OtherwiseSecureStorageError.Closed
is thrown and the encryption key remains unchanged. Changing the key means re-encryption of the whole database which can take a long time on larger databases.Throws
SecureStorageError.Closed
if the store is locked with its encryption key.Throws
SecureStorageError.EncryptionKeyChangeFailed
if an error occured while changing the encryption key.Declaration
Swift
public func changeEncryptionKey(with newEncryptionKeyData: Data) throws
Parameters
newEncryptionKeyData
the
SecureStore
’s new encryption key.
-
Execute single update statement
This method executes a single SQL update statement (i.e. any SQL that does not return results, such as
UPDATE
,INSERT
, orDELETE
). This method employssqlite3_prepare_v2
.try db.executeUpdate("INSERT INTO myTable VALUES ('value')")
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeUpdate(_ sql: String) throws
Parameters
sql
The SQL to be performed.
-
Execute single update statement
This method executes a single SQL update statement (i.e. any SQL that does not return results, such as
UPDATE
,INSERT
, orDELETE
). This method employssqlite3_prepare_v2
andsqlite3_bind
binding any?
placeholders in the SQL with the optional list of parameters.The optional values provided to this method must be
SQLiteDatatypeBridgeable
compliant types. The following table contains the binding relations for these types to SQLite3 types.Swift Type SQLite3 Type nil
NULL
Data
BLOB
Date
INTEGER
Int
,Int8
-Int64
,UInt
,UInt8
-UInt64
,Bool
INTEGER
Float
,Double
REAL
String
TEXT
More information can be found at http://www.sqlite.org/datatype3.html.
Date
objects are stored as timestamps in the format of seconds since 01.01.1970 without timezone information. Sub-second time information is lost.try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.typeConversionFailed
if a value in values could not be converted to its corresponding sqlite database type.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeUpdate(_ sql: String, values: [SQLiteDatatypeBridgeable?]) throws
Parameters
sql
The SQL to be performed, with optional
?
placeholders.values
An
Array<SQLiteDatatypeBridgeable?>
of objects to be used when binding values to the?
placeholders in the SQL statement. -
Execute single update statement
This method executes a single SQL update statement (i.e. any SQL that does not return results, such as
UPDATE
,INSERT
, orDELETE
). This method employssqlite3_prepare_v2
andsqlite3_bind
binding any:
placeholders in the SQL with the optional list of parameters.The optional values provided to this method must be
SQLiteDatatypeBridgeable
compliant types. SeeexecuteUpdate(sql:, values:)
for more information.try db.executeUpdate("INSERT INTO myTable VALUES (:value)", parameterDictionary: ["value" : 1])
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.typeConversionFailed
if a value in the parameterDictionary could not be converted to its corresponding sqlite database type.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeUpdate(_ sql: String, parameterDictionary: [String : SQLiteDatatypeBridgeable?]) throws
Parameters
sql
The SQL to be performed, with optional
:
placeholders.parameterDictionary
A
[String: SQLiteDatatypeBridgeable?]
of objects keyed by column placeholder names (without colons) that will be used when binding values to the:
placeholders in the SQL statement. -
Execute multiple SQL statements
This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the
sqlite3
command line.dump
command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This usessqlite3_exec
.- An application should never execute ‘CREATE TABLE AS’ via ‘executeStatements’. Instead, create an empty table with ‘CREATE TABLE’ and insert the data in a separate statement afterwards.
- If the application uses ‘executeStatements’, it should never expose it in a way that statements from an external source could be executed without proper validation.
For more information please read SAP Note 2696962.
let sql = "CREATE TABLE bulktest1 (id INTEGER PRIMARY KEY AUTOINCREMENT, x TEXT);" + "CREATE TABLE bulktest2 (id INTEGER PRIMARY KEY AUTOINCREMENT, y TEXT);" + "CREATE TABLE bulktest3 (id INTEGER PRIMARY KEY AUTOINCREMENT, z TEXT);" do { try db.executeStatements(sql) } catch let error { logger.error("An error occurred while executing statements.", error: error) }
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.backingStoreError(code:message)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeStatements(_ sql: String) throws
Parameters
sql
The SQL to be performed.
-
Execute multiple SQL statements with callback handler
This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the
sqlite3
command line.dump
command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This usessqlite3_exec
.- An application should never execute ‘CREATE TABLE AS’ via ‘executeStatements’. Instead, create an empty table with ‘CREATE TABLE’ and insert the data in a separate statement afterwards.
- If the application uses ‘executeStatements’, it should never expose it in a way that statements from an external source could be executed without proper validation.
For more information please read SAP Note 2696962.
let sql = "SELECT COUNT(*) AS count FROM bulktest1;" + "SELECT COUNT(*) AS count FROM bulktest2;" + "SELECT COUNT(*) AS count FROM bulktest3;" do { try db.executeStatements(sql) { resultDictionary in if let count = resultDictionary["count"] { self.logger.info("bulktest1 contains \(count) rows") return true } else { self.logger.info("Something went wrong. Stop executing all following statements.") return false } } } catch let error { logger.error("An error occurred while executing statements.", error: error) }
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeStatements(_ sql: String, resultBlock: @escaping (_ results: [String : String?]) -> Bool) throws
Parameters
sql
The SQL to be performed.
resultBlock
A block that will be called for any result row returned by any SQL statements. Note, if you supply this block, it must return a
Bool
value;true
upon success,false
upon failure (which will stop the bulk execution of the SQL). If a statement returns values, the block will be called with a Dictionary of the result row from the query as a parameter. The Dictionary is keyed with the column names and contains result values as if they were obtained withstringForColumn(columnName: String)
on a result set. -
Last insert rowid
Each entry in an SQLite table has a unique 64-bit signed integer key called the “rowid”. The rowid is always available as an undeclared column named
ROWID
,OID
, or_ROWID_
as long as those names are not also used by explicitly declared columns. If the table has a column of typeINTEGER PRIMARY KEY
then that column is another alias for the rowid.This routine returns the rowid of the most recent successful
INSERT
into the database from the database connection in the first argument. This routine records the last insert rowid of both ordinary tables and virtual tables. If no successfulINSERT
s have ever occurred on that database connection, zero is returned.Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.Declaration
Swift
public func lastInsertRowId() throws -> Int64
Return Value
The rowid of the last inserted row.
0
if no successfulINSERT
s have ever occurred. -
The number of rows changed by the previous SQL statement.
This function returns the number of database rows that were changed or inserted or deleted by the most recently completed SQL statement on the database. Only changes that are directly specified by the INSERT, UPDATE, or DELETE statement are counted.
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.Declaration
Swift
public func changedRowCount() throws -> Int
Return Value
The number of rows changed by the previous SQL statement.
-
Execute select statement
Executing queries returns an
SecureDatabaseResultSet
object if successful, and throws an error upon failure.try executeQuery("SELECT COUNT(*) FROM myTable")
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeQuery(_ sql: String) throws -> SecureDatabaseResultSet
Parameters
sql
The
SELECT
statement to be performed.Return Value
A
SecureDatabaseResultSet
for the result set upon success. -
Execute select statement
Executing queries returns an
SecureDatabaseResultSet
object if successful, and throws an error upon failure. The optional values provided to this method must beSQLiteDatatypeBridgeable
compliant types. SeeexecuteUpdate(sql:, values:)
for more information.try executeQuery("SELECT * FROM myTable WHERE columnName=?", values: ["MyValue"])
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.typeConversionFailed
if a value in values could not be converted to its corresponding sqlite database type.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeQuery(_ sql: String, values: [SQLiteDatatypeBridgeable?]) throws -> SecureDatabaseResultSet
Parameters
sql
The
SELECT
statement to be performed, with optional?
placeholders.values
An
Array<SQLiteDatatypeBridgeable?>
of objects to be used when binding values to the?
placeholders in the SQL statement.Return Value
A
SecureDatabaseResultSet
for the result set upon success. -
Execute select statement
Executing queries returns an
SecureDatabaseResultSet
object if successful, and throws an error upon failure. The optional values provided to this method must beSQLiteDatatypeBridgeable
compliant types. SeeexecuteUpdate(sql:, values:)
for more information.try executeQuery("SELECT * FROM myTable WHERE columnName=:value", values: ["value" : "MyValue"])
Throws
SecureStorageError.closed
if the SecureDatabaseStore is locked with its encryption key.SecureStorageError.typeConversionFailed
if a value in the parameterDictionary could not be converted to its corresponding sqlite database type.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func executeQuery(_ sql: String, parameterDictionary: [String : SQLiteDatatypeBridgeable?]) throws -> SecureDatabaseResultSet
Parameters
sql
The
SELECT
statement to be performed, with optional:
placeholders.parameterDictionary
A
[String: SQLiteDataTypConvertible?]
of objects keyed by column placeholder names (without colons) that will be used when binding values to the:
placeholders in the SQL statement.Return Value
A
SecureDatabaseResultSet
for the result set upon success. -
Begin an exclusive transaction. Therefore, exclusive locks are acquired on the database and no other connection will be able to read or write to the database until the transaction is complete.
Throws
SecureStorageError.closed
if theSecureDatabaseStore
is locked with its encryption key.SecureStorageError.backingStoreError(code:message)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func beginExclusiveTransaction() throws
-
Begin a deferred transaction. Therefore, no locks are acquired on the database until the database is first accessed.
Throws
SecureStorageError.closed
if theSecureDatabaseStore
is locked with its encryption key.SecureStorageError.backingStoreError(code:message:)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func beginDeferredTransaction() throws
-
Commit a transaction
Throws
SecureStorageError.closed
if theSecureDatabaseStore
is locked with its encryption key.SecureStorageError.backingStoreError(code:message)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func commit() throws
-
Rollback a transaction
Throws
SecureStorageError.closed
if theSecureDatabaseStore
is locked with its encryption key.SecureStorageError.backingStoreError(code:message)
if an error occured on the database store. Code corresponds to sqlite3 error codes (https://www.sqlite.org/c3ref/c_abort.html).
Declaration
Swift
public func rollback() throws
-
Identify whether currently in a transaction or not
Declaration
Swift
public func isInTransaction() throws -> Bool
Return Value
true
if currently within transaction;false
otherwise. -
Whether the database should cache statements or not.
true
if it should cache,false
otherwise.Declaration
Swift
public var shouldCacheStatements: Bool { get set }
-
Clear cached statements
Declaration
Swift
public func clearCachedStatements()
-
Whether the database has any open result sets.
true
if there are open result sets,false
otherwise.Declaration
Swift
public var hasOpenResultSets: Bool { get }
-
Close all open result sets
Declaration
Swift
public func closeOpenResultSets()
-
The default initialization handler for the SecureStore that does nothing end returns nil.
Declaration
Swift
public static func defaultInitializationHandler(_ store: SecureDatabaseStore) -> Error?