podman

Форк
0
288 строк · 8.2 Кб
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

9
package 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

19
int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x);
20
*/
21
import "C"
22

23
import (
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.
32
const (
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

39
type 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.
72
type TraceUserCallback func(TraceInfo) int
73

74
type TraceConfig struct {
75
	Callback        TraceUserCallback
76
	EventMask       uint32
77
	WantExpandedSQL bool
78
}
79

80
func 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

87
func 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
102
func 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

202
type traceMapEntry struct {
203
	config TraceConfig
204
}
205

206
var traceMapLock sync.Mutex
207
var traceMap = make(map[uintptr]traceMapEntry)
208

209
func 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

221
func 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
230
func 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.
246
func (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

277
func (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
}
289

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.