2

The page http://www.sqlite.org/threadsafe.html mentions:

Single-thread

Multi-thread

Serialized

How can we implement that threads in Swift. Or how to use sqlite3_config(SQLITE_CONFIG_MULTITHREAD in Swift

aaK
  • 103
  • 9

2 Answers2

3

Instead of sqlite3_config you can add the required options when opening the database. This is only available for sqlite3_open_v2, not for sqlite3_open or sqlite3_open16.

Here is an example:

let rc = sqlite3_open_v2(databasePath, &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, nil)
freytag
  • 4,769
  • 2
  • 27
  • 32
  • 1
    If I open database with `sqlite3_open_v2(databasePath, &db,SQLITE_OPEN_FULLMUTEX, nil)`, I can open in one process and write at same time that another process open and read? – Augusto Nov 27 '18 at 20:46
2

For anyone who ended up here looking for a way to call sqlite3_config (regardless of the threading question) using the built-in SQLite3 module...

The reason it's not available is that it uses ellipsis-style variadic arguments, which according to this are not supported in Swift. One way to get around this is to make your own C header with wrapper functions for calling sqlite3_config with whatever (non-variadic) argument arrangement you need.

For example, a "bridging header" with something like this covers all the valid call signatures you might need (as far as what the SQLite config options require):

#ifndef SqliteConfig_h
#define SqliteConfig_h

#import "sqlite3.h"

static inline int sqlite3_config_no_args(int op) {
    return sqlite3_config(op);
}

static inline int sqlite3_config_ptr(int op, void* arg1) {
    return sqlite3_config(op, arg1);
}

static inline int sqlite3_config_int(int op, int arg1) {
    return sqlite3_config(op, arg1);
}

static inline int sqlite3_config_ptr_int_int(int op, void* arg1, int arg2, int arg3) {
    return sqlite3_config(op, arg1, arg2, arg3);
}

static inline int sqlite3_config_int_int(int op, int arg1, int arg2) {
    return sqlite3_config(op, arg1, arg2);
}

static inline int sqlite3_config_ptr_ptr(int op, void* arg1, void* arg2) {
    return sqlite3_config(op, arg1, arg2);
}

#endif /* SqliteConfig_h */

Maybe there are cleaner or more idiomatic ways to create these kinds of wrappers (rather than static-inline functions in a bridging header). I'm not an expert in this area. But considering they're just wrapping calls, it seems like a good lightweight workaround.

You can search around for more info on bridging headers if you don't know how to create one. But in short: You can create a bridging header "manually" (after creating your .h file) by going to Build Settings/Swift Compiler - General/Objective-C Bridging Header and setting the value to the header filename you created. If you don't do that, your .h file is ignored. Or you could create one automatically (without going into the compiler settings) by dragging a dummy .c file into the project, deleting that file, and then editing the header it created. Or do that with a "real" C file if you prefer your code there rather than inline.

With that in place, here's an example of using sqlite3_config to install your own logging callback:

import SQLite3

func errorLogCallback(_: OpaquePointer?, iErrCode: Int, zMsg: UnsafePointer<CChar>)
{
    let s = String(cString: zMsg)
    print("LOG:", iErrCode, s)
}

let cCallbackPtr: @convention(c) (OpaquePointer?, Int, UnsafePointer<CChar>) -> () = errorLogCallback
let rawPtr = unsafeBitCast(cCallbackPtr, to: UnsafeMutableRawPointer.self)
sqlite3_config_ptr_ptr(SQLITE_CONFIG_LOG, rawPtr, nil)
ecp
  • 323
  • 3
  • 7