amplify-swift/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/LocalStorage/SQLiteLocalStorageAdapter.s...

111 lines
4.0 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Amplify
import Foundation
import SQLite
/// Local storage adapter that implements local storage using SQLite.swift
final class SQLiteLocalStorageAdapter: SQLStorageProtocol {
private var connection: Connection?
private let dbFilePath: URL
private let fileManager: FileManagerBehaviour
var diskBytesUsed: Byte {
return fileManager.fileSize(for: dbFilePath)
}
/// Initializer
/// - Parameters
/// - prefixPath: A prefix to be used for the database path. Defaults to none.
/// - databaseName: The database name
/// - fileManager: A FileManagerBehaviour instance to interact with the disk. Defaults to FileManager.default
init(prefixPath: String = "",
databaseName: String,
fileManager: FileManagerBehaviour = FileManager.default) throws {
let dbDirectoryPath = try Self.getTmpPath()
.appendingPathComponent(prefixPath)
var dbFilePath = dbDirectoryPath.appendingPathComponent(databaseName)
if !fileManager.fileExists(atPath: dbDirectoryPath.path) {
try fileManager.createDirectory(atPath: dbDirectoryPath.path,
withIntermediateDirectories: true)
}
let connection: Connection
do {
connection = try Connection(dbFilePath.path)
var urlResourceValues = URLResourceValues()
urlResourceValues.isExcludedFromBackup = true
try dbFilePath.setResourceValues(urlResourceValues)
} catch {
throw LocalStorageError.invalidStorage(path: dbFilePath.absoluteString, error)
}
self.connection = connection
self.dbFilePath = dbFilePath
self.fileManager = fileManager
try initializeDatabase(connection: connection)
}
/// Initilizes the database and create the table if it doesn't already exists
/// - Parameter connection: SQLite connection
private func initializeDatabase(connection: Connection) throws {
log.debug("Initializing database connection: \(String(describing: connection))")
let databaseInitializationStatement = """
pragma auto_vacuum = full;
pragma encoding = "utf-8";
pragma foreign_keys = on;
pragma case_sensitive_like = off;
"""
try connection.execute(databaseInitializationStatement)
}
/// Get document path
/// - Parameter fileManager: The FileManagerBehaviour instance used to interact with the disk
/// - Returns: Optional URL to the Document path
private static func getTmpPath() throws -> URL {
guard let tmpUrl = URL(string: NSTemporaryDirectory()) else {
throw LocalStorageError.fileSystemError(description: "Could not create the database at tmp directory")
}
return tmpUrl
}
/// Create a SQL table
/// - Parameter statement: SQL statement to create a table
func createTable(_ statement: String) throws {
guard let connection = connection else {
throw LocalStorageError.missingConnection
}
do {
try connection.execute(statement)
} catch {
throw LocalStorageError.invalidOperation(causedBy: error)
}
}
/// Executes a SQL query
/// - Parameters:
/// - statement: SQL query statement
/// - bindings: A collection of SQL bindings to prepare with the query statement
/// - Returns: A SQL statement result from the query
func executeQuery(_ statement: String, _ bindings: [Binding?]) throws -> Statement {
guard let connection = connection else {
throw LocalStorageError.missingConnection
}
do {
let statement = try connection.prepare(statement).run(bindings)
return statement
} catch {
throw LocalStorageError.invalidOperation(causedBy: error)
}
}
}
extension SQLiteLocalStorageAdapter: DefaultLogger { }