朧の.Netの足跡
問合せ先:support@oborodukiyo.info サイト内検索はこちら 
Swift Swift4からSQLite3でWALが設定されている時のアクセスの仕方





Swift4からSQLite3のデータベースでWALモードのものに実はアクセスは普通にできますが、サンドボックスの時に問題が起きます。
サンドボックス時に一般的なフォルダにsqliteのファイルがあるとWALモードの場合は一度目のアクセスでデータベースは開けるのですが、SQL文のチェックの為に二度目のアクセスをするとエラーになります。
これを回避する為に私が取ったのはsqliteファイルをキャッシュフォルダにコピーをして、そのキャッシュフォルダにコピーしたsqliteに対してSQL文を実行し、アプリを終了する時にもともとあったsqliteファイルを削除して、先ほどのコピーしたsqliteファイルを元々の場所にコピーをして戻して、キャッシュフォルダのsqliteファイルを削除します。

データベースを読み込む時の処理例

                                let op = NSOpenPanel()
        op.canChooseFiles = true
        op.canChooseDirectories = false
        op.allowsMultipleSelection = false
        op.allowedFileTypes = ["sqlite"]
        op.directoryURL = myURL
        
        if op.runModal() != NSApplication.ModalResponse.OK {
            return
            
        }

        //ファイルのコピーの処理
        let manager = FileManager.default
        //キャッシュフォルダのパスの取得
        let localPath = manager.urls(for: .cachesDirectory, in: .userDomainMask).first!
        //キャッシュにコピーする時のファイル名は固定にしています。
        let cacheFolder = "sample.sqlite"
        let localBackupDirectory = localPath.appendingPathComponent(cacheFolder)
        
        do {
            //コピー処理
            try manager.copyItem(at: op.url!, to: localBackupDirectory)
            
        } catch {
            print("Copy files Error: \(error)")
            return
        }
        
        //以下はSQL文の実行例ですが、sqlite3のAPIで直に操作しています。
        var db: OpaquePointer? = nil
        
        let sqlRet0 = sqlite3_open_v2(localBackupDirectory.absoluteString, &db, SQLITE_OPEN_URI | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE, nil)
        if sqlRet0 == SQLITE_OK {
            print("Database is opened.")
            
        } else {
            print("Database couldn't be opened.")
            return
        }

        let queryStatementString = "SELECT id, name FROM groups"
        var queryStatement: OpaquePointer? = nil
        let sqlRet1 = sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil)
        print(sqlRet1)
        if sqlRet1 == SQLITE_OK {
            print("SQL statement is OK.")
            while (sqlite3_step(queryStatement) == SQLITE_ROW) {
                let id = sqlite3_column_int(queryStatement, 0)
                let titleCol1 = sqlite3_column_text(queryStatement, 1)
                var title: String? = nil
                if titleCol1 == nil {
                    title = ""
                } else {
                    title = String(cString: titleCol1!)
                }
                print("id: \(id), name: \(title!)")
            }
        }
        //sqlite3の後処理
        sqlite3_finalize(queryStatement)
        sqlite3_close(db)

        //元の位置に戻すための処理
                
        do {
            //元々のファイルを削除。上書きコピーができないからです。
            try manager.removeItem(at: op.url!)
            //キャッシュフォルダのファイルを元々の場所にコピー
            try manager.copyItem(at: localBackupDirectory!, to: op.url!)
            //キャッシュフォルダのファイルを削除。これが残っていると次にコピーできない。
            try manager.removeItem(at: localBackupDirectory!)
        } catch {
            print("\(error)")
            
            let panel = NSAlert()            
            panel.messageText = "ファイルのコピーに失敗しました。"
            panel.runModal()
            return
        }
        








良いやや良い普通やや悪い悪い

投稿日時評価コメント