-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdb.lua
301 lines (218 loc) · 6.39 KB
/
db.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
-- db.lua
-- Support library for both vgdb.lua and valgrind.lua, for SQLite DB access
local lsqlite = require("lsqlite3")
local os = os
local _pkg = {}
--- Executes the SQL statement, substituting "?" in the SQL with the specified params
-- Calls a_Callback for each row
-- The callback receives a dictionary table containing the row values (stmt:nrows())
-- The RowID callback receives the RowID of the inserted row (if applicable)
-- Returns false and error message on failure, or true on success
function _pkg:executeStatement(a_SQL, a_Params, a_Callback, a_RowIDCallback)
-- Check params:
assert(self)
assert(self.m_DB)
assert(a_SQL)
assert((a_Params == nil) or (type(a_Params) == "table"))
assert(self.m_DB)
assert((a_Callback == nil) or (type(a_Callback) == "function"))
assert((a_RowIDCallback == nil) or (type(a_RowIDCallback) == "function"))
-- Prepare the statement (SQL-compile):
local Stmt, ErrCode, ErrMsg = self.m_DB:prepare(a_SQL)
if (Stmt == nil) then
ErrMsg = (ErrCode or "<unknown>") .. " (" .. (ErrMsg or "<no message>") .. ")"
LOGWARNING("Cannot prepare SQL \"" .. a_SQL .. "\": " .. ErrMsg)
LOGWARNING(" Params = {" .. table.concat(a_Params or {}, ", ") .. "}")
return nil, ErrMsg
end
-- Bind the values into the statement:
if (a_Params) then
ErrCode = Stmt:bind_values(unpack(a_Params))
if ((ErrCode ~= sqlite3.OK) and (ErrCode ~= sqlite3.DONE)) then
ErrMsg = (ErrCode or "<unknown>") .. " (" .. (self.m_DB:errmsg() or "<no message>") .. ")"
LOGWARNING("Cannot bind values to statement \"" .. a_SQL .. "\": " .. ErrMsg)
Stmt:finalize()
return nil, ErrMsg
end
end
-- Step the statement:
if not(a_Callback) then
ErrCode = Stmt:step()
if ((ErrCode ~= sqlite3.ROW) and (ErrCode ~= sqlite3.DONE)) then
ErrMsg = (ErrCode or "<unknown>") .. " (" .. (self.m_DB:errmsg() or "<no message>") .. ")"
LOGWARNING("Cannot step statement \"" .. a_SQL .. "\": " .. ErrMsg)
Stmt:finalize()
return nil, ErrMsg
end
if (a_RowIDCallback) then
a_RowIDCallback(self.m_DB:last_insert_rowid())
end
else
-- Iterate over all returned rows:
for v in Stmt:nrows() do
a_Callback(v)
end
if (a_RowIDCallback) then
a_RowIDCallback(self.m_DB:last_insert_rowid())
end
end
Stmt:finalize()
return true
end
--- Calls the specified callback for each instance
-- The callback receives a table with members "Pid", "CmdLine" and "StartTime"
-- Returns true on success, false and optional message on failure
function _pkg:forEachInstance(a_Callback)
assert(self)
assert(type(a_Callback) == "function")
return self:executeStatement(
"SELECT * FROM ValgrindInstances",
nil,
a_Callback
)
end
--- Returns a table describing the valgrind instance specified by its pid
-- Returns nil and optional error message on error
-- Returns empty table if instance not found
-- The table returned on success has "Pid", "ValgrindParams", "CmdLine" and "StartTime" members
function _pkg:getInstance(a_Pid)
assert(self)
assert(tonumber(a_Pid))
local inst = {}
local isSuccess, msg = self:executeStatement(
"SELECT * FROM ValgrindInstances",
nil,
function (a_Values)
inst = a_Values
end
)
if not(isSuccess) then
return nil, msg
end
return inst
end
--- If there is only a single Valgrind instance, returns its pid
-- Returns nil and optional message on failure (incl. multiple instances)
function _pkg:getSingleInstancePid()
assert(self)
local singlePid = -1
local isSuccess, msg = self:executeStatement(
"SELECT Pid FROM ValgrindInstances",
nil,
function (a_Values)
if (singlePid == -1) then
-- This is the first Pid encountered, remember it
singlePid = a_Values.Pid
else
-- There are multiple Pids, set the output to "nil"
singlePid = nil
return false
end
end
)
if not(isSuccess) then
return nil, msg
end
return singlePid
end
--- Initializes the module
-- Opens the DB and creates tables, if needed
-- Returns true on success, nil and optional message on failure
function _pkg:init()
assert(self)
-- Open the DB file:
local DB, err = lsqlite.open("valgrind.sqlite")
if not(DB) then
return nil, err
end
-- Initialize the tables:
DB:exec([[CREATE TABLE IF NOT EXISTS ValgrindInstances (
Pid INTEGER PRIMARY KEY,
ValgrindParams TEXT,
CmdLine TEXT,
TimeUnits TEXT,
StartTime DATETIME
)
]])
-- All OK
self.m_DB = DB
return true
end
--- Returns whether the Valgrind instance specified by its Pid is valid
-- Returns nil and optional message on failure
function _pkg:isValidInstance(a_Pid)
assert(self)
assert(tonumber(a_Pid))
local isValid = false
local isSuccess, msg = self:executeStatement(
"SELECT Pid FROM ValgrindInstances WHERE Pid = ?",
{a_Pid},
function (a_Values)
if (a_Values.Pid == a_Pid) then
isValid = true
end
end
)
if not(isSuccess) then
return nil, msg
end
return isValid
end
--- Inserts a new Valgrind instance to the DB
-- Returns the PID of the instance, as assigned by the DB
-- Returns nil and optional message on error
function _pkg:insertNewInstance(a_ValgrindParams, a_CmdLine, a_TimeUnits)
assert(self)
assert(type(a_ValgrindParams) == "string")
assert(type(a_CmdLine) == "string")
assert(type(a_TimeUnits) == "string")
local pid = -1
local isSuccess, msg = self:executeStatement(
"INSERT INTO ValgrindInstances (ValgrindParams, CmdLine, TimeUnits, StartTime) VALUES (?, ?, ?, ?)",
{
a_ValgrindParams,
a_CmdLine,
a_TimeUnits,
os.clock()
},
nil,
function (a_RowID)
pid = a_RowID
end
)
if (not(isSuccess) or (pid < 0)) then
return nil, msg
end
return pid
end
--- Returns whether if the Valgrind instance specified by its Pid is scheduled for termination
-- Returns nil and optional message on failure
function _pkg:shouldInstanceTerminate(a_Pid)
assert(self)
assert(tonumber(a_Pid))
local isThere = false
local isSuccess, msg = self:executeStatement(
"SELECT * FROM ValgrindInstances WHERE Pid = ?",
{a_Pid},
function (a_Values)
if (a_Values.Pid == a_Pid) then
isThere = true
end
end
)
if not(isSuccess) then
return nil, msg
end
return not(isThere)
end
--- Schedules the Valgrind instance specified by its Pid for termination
-- Returns true on success, nil and optional message on failure
function _pkg:terminateInstance(a_Pid)
assert(self)
assert(tonumber(a_Pid))
return self:executeStatement(
"DELETE FROM ValgrindInstances WHERE Pid = ?",
{a_Pid}
)
end
return _pkg