package sqlite3
import (
"context"
"fmt"
"strconv"
"sync/atomic"
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/vfs"
)
func (c *Conn ) Config (op DBConfig , arg ...bool ) (bool , error ) {
if op < DBCONFIG_ENABLE_FKEY || op > DBCONFIG_REVERSE_SCANORDER {
return false , MISUSE
}
defer c .arena .mark ()()
argsPtr := c .arena .new (intlen + ptrlen )
var flag int32
switch {
case len (arg ) == 0 :
flag = -1
case arg [0 ]:
flag = 1
}
util .Write32 (c .mod , argsPtr +0 *ptrlen , flag )
util .Write32 (c .mod , argsPtr +1 *ptrlen , argsPtr )
rc := res_t (c .call ("sqlite3_db_config" , stk_t (c .handle ),
stk_t (op ), stk_t (argsPtr )))
return util .ReadBool (c .mod , argsPtr ), c .error (rc )
}
var defaultLogger atomic .Pointer [func (code ExtendedErrorCode , msg string )]
func ConfigLog (cb func (code ExtendedErrorCode , msg string )) {
defaultLogger .Store (&cb )
}
func (c *Conn ) ConfigLog (cb func (code ExtendedErrorCode , msg string )) error {
var enable int32
if cb != nil {
enable = 1
}
rc := res_t (c .call ("sqlite3_config_log_go" , stk_t (enable )))
if err := c .error (rc ); err != nil {
return err
}
c .log = cb
return nil
}
func logCallback(ctx context .Context , mod api .Module , _ ptr_t , iCode res_t , zMsg ptr_t ) {
if c , ok := ctx .Value (connKey {}).(*Conn ); ok && c .log != nil {
msg := util .ReadString (mod , zMsg , _MAX_LENGTH )
c .log (xErrorCode (iCode ), msg )
}
}
func (c *Conn ) Log (code ExtendedErrorCode , format string , a ...any ) {
if c .log != nil {
c .log (code , fmt .Sprintf (format , a ...))
}
}
func (c *Conn ) FileControl (schema string , op FcntlOpcode , arg ...any ) (any , error ) {
defer c .arena .mark ()()
ptr := c .arena .new (max (ptrlen , intlen ))
var schemaPtr ptr_t
if schema != "" {
schemaPtr = c .arena .string (schema )
}
var rc res_t
var ret any
switch op {
default :
return nil , MISUSE
case FCNTL_RESET_CACHE , FCNTL_NULL_IO :
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), 0 ))
case FCNTL_PERSIST_WAL , FCNTL_POWERSAFE_OVERWRITE :
var flag int32
switch {
case len (arg ) == 0 :
flag = -1
case arg [0 ]:
flag = 1
}
util .Write32 (c .mod , ptr , flag )
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
ret = util .ReadBool (c .mod , ptr )
case FCNTL_CHUNK_SIZE :
util .Write32 (c .mod , ptr , int32 (arg [0 ].(int )))
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
case FCNTL_RESERVE_BYTES :
bytes := -1
if len (arg ) > 0 {
bytes = arg [0 ].(int )
}
util .Write32 (c .mod , ptr , int32 (bytes ))
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
ret = int (util .Read32 [int32 ](c .mod , ptr ))
case FCNTL_DATA_VERSION :
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
ret = util .Read32 [uint32 ](c .mod , ptr )
case FCNTL_LOCKSTATE :
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
ret = util .Read32 [vfs .LockLevel ](c .mod , ptr )
case FCNTL_VFS_POINTER :
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
if rc == _OK {
const zNameOffset = 16
ptr = util .Read32 [ptr_t ](c .mod , ptr )
ptr = util .Read32 [ptr_t ](c .mod , ptr +zNameOffset )
name := util .ReadString (c .mod , ptr , _MAX_NAME )
ret = vfs .Find (name )
}
case FCNTL_FILE_POINTER , FCNTL_JOURNAL_POINTER :
rc = res_t (c .call ("sqlite3_file_control" ,
stk_t (c .handle ), stk_t (schemaPtr ),
stk_t (op ), stk_t (ptr )))
if rc == _OK {
const fileHandleOffset = 4
ptr = util .Read32 [ptr_t ](c .mod , ptr )
ptr = util .Read32 [ptr_t ](c .mod , ptr +fileHandleOffset )
ret = util .GetHandle (c .ctx , ptr )
}
}
if err := c .error (rc ); err != nil {
return nil , err
}
return ret , nil
}
func (c *Conn ) Limit (id LimitCategory , value int ) int {
v := int32 (c .call ("sqlite3_limit" , stk_t (c .handle ), stk_t (id ), stk_t (value )))
return int (v )
}
func (c *Conn ) SetAuthorizer (cb func (action AuthorizerActionCode , name3rd , name4th , schema , inner string ) AuthorizerReturnCode ) error {
var enable int32
if cb != nil {
enable = 1
}
rc := res_t (c .call ("sqlite3_set_authorizer_go" , stk_t (c .handle ), stk_t (enable )))
if err := c .error (rc ); err != nil {
return err
}
c .authorizer = cb
return nil
}
func authorizerCallback(ctx context .Context , mod api .Module , pDB ptr_t , action AuthorizerActionCode , zName3rd , zName4th , zSchema , zInner ptr_t ) (rc AuthorizerReturnCode ) {
if c , ok := ctx .Value (connKey {}).(*Conn ); ok && c .handle == pDB && c .authorizer != nil {
var name3rd , name4th , schema , inner string
if zName3rd != 0 {
name3rd = util .ReadString (mod , zName3rd , _MAX_NAME )
}
if zName4th != 0 {
name4th = util .ReadString (mod , zName4th , _MAX_NAME )
}
if zSchema != 0 {
schema = util .ReadString (mod , zSchema , _MAX_NAME )
}
if zInner != 0 {
inner = util .ReadString (mod , zInner , _MAX_NAME )
}
rc = c .authorizer (action , name3rd , name4th , schema , inner )
}
return rc
}
func (c *Conn ) Trace (mask TraceEvent , cb func (evt TraceEvent , arg1 any , arg2 any ) error ) error {
rc := res_t (c .call ("sqlite3_trace_go" , stk_t (c .handle ), stk_t (mask )))
if err := c .error (rc ); err != nil {
return err
}
c .trace = cb
return nil
}
func traceCallback(ctx context .Context , mod api .Module , evt TraceEvent , pDB , pArg1 , pArg2 ptr_t ) (rc res_t ) {
if c , ok := ctx .Value (connKey {}).(*Conn ); ok && c .handle == pDB && c .trace != nil {
var arg1 , arg2 any
if evt == TRACE_CLOSE {
arg1 = c
} else {
for _ , s := range c .stmts {
if pArg1 == s .handle {
arg1 = s
switch evt {
case TRACE_STMT :
arg2 = s .SQL ()
case TRACE_PROFILE :
arg2 = util .Read64 [int64 ](mod , pArg2 )
}
break
}
}
}
if arg1 != nil {
_, rc = errorCode (c .trace (evt , arg1 , arg2 ), ERROR )
}
}
return rc
}
func (c *Conn ) WALCheckpoint (schema string , mode CheckpointMode ) (nLog , nCkpt int , err error ) {
if c .interrupt .Err () != nil {
return 0 , 0 , INTERRUPT
}
defer c .arena .mark ()()
nLogPtr := c .arena .new (ptrlen )
nCkptPtr := c .arena .new (ptrlen )
schemaPtr := c .arena .string (schema )
rc := res_t (c .call ("sqlite3_wal_checkpoint_v2" ,
stk_t (c .handle ), stk_t (schemaPtr ), stk_t (mode ),
stk_t (nLogPtr ), stk_t (nCkptPtr )))
nLog = int (util .Read32 [int32 ](c .mod , nLogPtr ))
nCkpt = int (util .Read32 [int32 ](c .mod , nCkptPtr ))
return nLog , nCkpt , c .error (rc )
}
func (c *Conn ) WALAutoCheckpoint (pages int ) error {
rc := res_t (c .call ("sqlite3_wal_autocheckpoint" , stk_t (c .handle ), stk_t (pages )))
return c .error (rc )
}
func (c *Conn ) WALHook (cb func (db *Conn , schema string , pages int ) error ) {
var enable int32
if cb != nil {
enable = 1
}
c .call ("sqlite3_wal_hook_go" , stk_t (c .handle ), stk_t (enable ))
c .wal = cb
}
func walCallback(ctx context .Context , mod api .Module , _ , pDB , zSchema ptr_t , pages int32 ) (rc res_t ) {
if c , ok := ctx .Value (connKey {}).(*Conn ); ok && c .handle == pDB && c .wal != nil {
schema := util .ReadString (mod , zSchema , _MAX_NAME )
err := c .wal (c , schema , int (pages ))
_, rc = errorCode (err , ERROR )
}
return rc
}
func (c *Conn ) AutoVacuumPages (cb func (schema string , dbPages , freePages , bytesPerPage uint ) uint ) error {
var funcPtr ptr_t
if cb != nil {
funcPtr = util .AddHandle (c .ctx , cb )
}
rc := res_t (c .call ("sqlite3_autovacuum_pages_go" , stk_t (c .handle ), stk_t (funcPtr )))
return c .error (rc )
}
func autoVacuumCallback(ctx context .Context , mod api .Module , pApp , zSchema ptr_t , nDbPage , nFreePage , nBytePerPage uint32 ) uint32 {
fn := util .GetHandle (ctx , pApp ).(func (schema string , dbPages , freePages , bytesPerPage uint ) uint )
schema := util .ReadString (mod , zSchema , _MAX_NAME )
return uint32 (fn (schema , uint (nDbPage ), uint (nFreePage ), uint (nBytePerPage )))
}
func (c *Conn ) SoftHeapLimit (n int64 ) int64 {
return int64 (c .call ("sqlite3_soft_heap_limit64" , stk_t (n )))
}
func (c *Conn ) HardHeapLimit (n int64 ) int64 {
return int64 (c .call ("sqlite3_hard_heap_limit64" , stk_t (n )))
}
func (c *Conn ) EnableChecksums (schema string ) error {
r , err := c .FileControl (schema , FCNTL_RESERVE_BYTES )
if err != nil {
return err
}
if r == 8 {
return nil
}
if r == 0 {
_, err = c .FileControl (schema , FCNTL_RESERVE_BYTES , 8 )
if err != nil {
return err
}
r , err = c .FileControl (schema , FCNTL_RESERVE_BYTES )
if err != nil {
return err
}
}
if r != 8 {
return util .ErrorString ("sqlite3: reserve bytes must be 8, is: " + strconv .Itoa (r .(int )))
}
if schema != "" {
err = c .Exec (`VACUUM ` + QuoteIdentifier (schema ))
} else {
err = c .Exec (`VACUUM` )
}
if err != nil {
return err
}
_, _, err = c .WALCheckpoint (schema , CHECKPOINT_FULL )
return err
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .