a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 8.4 kB view raw
1// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. 2// 3// Use of this source code is governed by an MIT-style 4// license that can be found in the LICENSE file. 5 6//go:build sqlite_trace || trace 7// +build sqlite_trace trace 8 9package sqlite3 10 11/* 12#ifndef USE_LIBSQLITE3 13#include "sqlite3-binding.h" 14#else 15#include <sqlite3.h> 16#endif 17#include <stdlib.h> 18 19int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); 20*/ 21import "C" 22 23import ( 24 "fmt" 25 "strings" 26 "sync" 27 "unsafe" 28) 29 30// Trace... constants identify the possible events causing callback invocation. 31// Values are same as the corresponding SQLite Trace Event Codes. 32const ( 33 TraceStmt = uint32(C.SQLITE_TRACE_STMT) 34 TraceProfile = uint32(C.SQLITE_TRACE_PROFILE) 35 TraceRow = uint32(C.SQLITE_TRACE_ROW) 36 TraceClose = uint32(C.SQLITE_TRACE_CLOSE) 37) 38 39type TraceInfo struct { 40 // Pack together the shorter fields, to keep the struct smaller. 41 // On a 64-bit machine there would be padding 42 // between EventCode and ConnHandle; having AutoCommit here is "free": 43 EventCode uint32 44 AutoCommit bool 45 ConnHandle uintptr 46 47 // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: 48 // identifier for a prepared statement: 49 StmtHandle uintptr 50 51 // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: 52 // (1) either the unexpanded SQL text of the prepared statement, or 53 // an SQL comment that indicates the invocation of a trigger; 54 // (2) expanded SQL, if requested and if (1) is not an SQL comment. 55 StmtOrTrigger string 56 ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) 57 58 // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: 59 // estimated number of nanoseconds that the prepared statement took to run: 60 RunTimeNanosec int64 61 62 DBError Error 63} 64 65// TraceUserCallback gives the signature for a trace function 66// provided by the user (Go application programmer). 67// SQLite 3.14 documentation (as of September 2, 2016) 68// for SQL Trace Hook = sqlite3_trace_v2(): 69// The integer return value from the callback is currently ignored, 70// though this may change in future releases. Callback implementations 71// should return zero to ensure future compatibility. 72type TraceUserCallback func(TraceInfo) int 73 74type TraceConfig struct { 75 Callback TraceUserCallback 76 EventMask uint32 77 WantExpandedSQL bool 78} 79 80func fillDBError(dbErr *Error, db *C.sqlite3) { 81 // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) 82 dbErr.Code = ErrNo(C.sqlite3_errcode(db)) 83 dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) 84 dbErr.err = C.GoString(C.sqlite3_errmsg(db)) 85} 86 87func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { 88 if pStmt == nil { 89 panic("No SQLite statement pointer in P arg of trace_v2 callback") 90 } 91 92 expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) 93 defer C.sqlite3_free(unsafe.Pointer(expSQLiteCStr)) 94 if expSQLiteCStr == nil { 95 fillDBError(&info.DBError, db) 96 return 97 } 98 info.ExpandedSQL = C.GoString(expSQLiteCStr) 99} 100 101//export traceCallbackTrampoline 102func traceCallbackTrampoline( 103 traceEventCode C.uint, 104 // Parameter named 'C' in SQLite docs = Context given at registration: 105 ctx unsafe.Pointer, 106 // Parameter named 'P' in SQLite docs (Primary event data?): 107 p unsafe.Pointer, 108 // Parameter named 'X' in SQLite docs (eXtra event data?): 109 xValue unsafe.Pointer) C.int { 110 111 eventCode := uint32(traceEventCode) 112 113 if ctx == nil { 114 panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) 115 } 116 117 contextDB := (*C.sqlite3)(ctx) 118 connHandle := uintptr(ctx) 119 120 var traceConf TraceConfig 121 var found bool 122 if eventCode == TraceClose { 123 // clean up traceMap: 'pop' means get and delete 124 traceConf, found = popTraceMapping(connHandle) 125 } else { 126 traceConf, found = lookupTraceMapping(connHandle) 127 } 128 129 if !found { 130 panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", 131 connHandle, eventCode)) 132 } 133 134 var info TraceInfo 135 136 info.EventCode = eventCode 137 info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) 138 info.ConnHandle = connHandle 139 140 switch eventCode { 141 case TraceStmt: 142 info.StmtHandle = uintptr(p) 143 144 var xStr string 145 if xValue != nil { 146 xStr = C.GoString((*C.char)(xValue)) 147 } 148 info.StmtOrTrigger = xStr 149 if !strings.HasPrefix(xStr, "--") { 150 // Not SQL comment, therefore the current event 151 // is not related to a trigger. 152 // The user might want to receive the expanded SQL; 153 // let's check: 154 if traceConf.WantExpandedSQL { 155 fillExpandedSQL(&info, contextDB, p) 156 } 157 } 158 159 case TraceProfile: 160 info.StmtHandle = uintptr(p) 161 162 if xValue == nil { 163 panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") 164 } 165 166 info.RunTimeNanosec = *(*int64)(xValue) 167 168 // sample the error //TODO: is it safe? is it useful? 169 fillDBError(&info.DBError, contextDB) 170 171 case TraceRow: 172 info.StmtHandle = uintptr(p) 173 174 case TraceClose: 175 handle := uintptr(p) 176 if handle != info.ConnHandle { 177 panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", 178 handle, info.ConnHandle)) 179 } 180 181 default: 182 // Pass unsupported events to the user callback (if configured); 183 // let the user callback decide whether to panic or ignore them. 184 } 185 186 // Do not execute user callback when the event was not requested by user! 187 // Remember that the Close event is always selected when 188 // registering this callback trampoline with SQLite --- for cleanup. 189 // In the future there may be more events forced to "selected" in SQLite 190 // for the driver's needs. 191 if traceConf.EventMask&eventCode == 0 { 192 return 0 193 } 194 195 r := 0 196 if traceConf.Callback != nil { 197 r = traceConf.Callback(info) 198 } 199 return C.int(r) 200} 201 202type traceMapEntry struct { 203 config TraceConfig 204} 205 206var traceMapLock sync.Mutex 207var traceMap = make(map[uintptr]traceMapEntry) 208 209func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { 210 traceMapLock.Lock() 211 defer traceMapLock.Unlock() 212 213 oldEntryCopy, found := traceMap[connHandle] 214 if found { 215 panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", 216 traceConf, connHandle, oldEntryCopy.config)) 217 } 218 traceMap[connHandle] = traceMapEntry{config: traceConf} 219} 220 221func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { 222 traceMapLock.Lock() 223 defer traceMapLock.Unlock() 224 225 entryCopy, found := traceMap[connHandle] 226 return entryCopy.config, found 227} 228 229// 'pop' = get and delete from map before returning the value to the caller 230func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { 231 traceMapLock.Lock() 232 defer traceMapLock.Unlock() 233 234 entryCopy, found := traceMap[connHandle] 235 if found { 236 delete(traceMap, connHandle) 237 } 238 return entryCopy.config, found 239} 240 241// SetTrace installs or removes the trace callback for the given database connection. 242// It's not named 'RegisterTrace' because only one callback can be kept and called. 243// Calling SetTrace a second time on same database connection 244// overrides (cancels) any prior callback and all its settings: 245// event mask, etc. 246func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { 247 connHandle := uintptr(unsafe.Pointer(c.db)) 248 249 _, _ = popTraceMapping(connHandle) 250 251 if requested == nil { 252 // The traceMap entry was deleted already by popTraceMapping(): 253 // can disable all events now, no need to watch for TraceClose. 254 err := c.setSQLiteTrace(0) 255 return err 256 } 257 258 reqCopy := *requested 259 260 // Disable potentially expensive operations 261 // if their result will not be used. We are doing this 262 // just in case the caller provided nonsensical input. 263 if reqCopy.EventMask&TraceStmt == 0 { 264 reqCopy.WantExpandedSQL = false 265 } 266 267 addTraceMapping(connHandle, reqCopy) 268 269 // The callback trampoline function does cleanup on Close event, 270 // regardless of the presence or absence of the user callback. 271 // Therefore it needs the Close event to be selected: 272 actualEventMask := uint(reqCopy.EventMask | TraceClose) 273 err := c.setSQLiteTrace(actualEventMask) 274 return err 275} 276 277func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { 278 rv := C.sqlite3_trace_v2(c.db, 279 C.uint(sqliteEventMask), 280 (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), 281 unsafe.Pointer(c.db)) // Fourth arg is same as first: we are 282 // passing the database connection handle as callback context. 283 284 if rv != C.SQLITE_OK { 285 return c.lastError() 286 } 287 return nil 288}