Skip to content

Commit 4afdedf

Browse files
committed
add capability to trace queries
1 parent 916dcac commit 4afdedf

2 files changed

Lines changed: 128 additions & 1 deletion

File tree

src/database.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ void Database::Init(Handle<Object> target) {
2525
NODE_SET_PROTOTYPE_METHOD(constructor_template, "exec", Exec);
2626
NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", Serialize);
2727
NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);
28+
NODE_SET_PROTOTYPE_METHOD(constructor_template, "configure", Configure);
2829

2930
target->Set(String::NewSymbol("Database"),
3031
constructor_template->GetFunction());
@@ -301,6 +302,69 @@ Handle<Value> Database::Parallelize(const Arguments& args) {
301302
return args.This();
302303
}
303304

305+
Handle<Value> Database::Configure(const Arguments& args) {
306+
HandleScope scope;
307+
Database* db = ObjectWrap::Unwrap<Database>(args.This());
308+
309+
REQUIRE_ARGUMENTS(2);
310+
311+
if (args[0]->Equals(String::NewSymbol("trace"))) {
312+
Local<Function> handle;
313+
Baton* baton = new Baton(db, handle);
314+
db->Schedule(RegisterTraceCallback, baton);
315+
}
316+
else {
317+
ThrowException(Exception::Error(String::Concat(
318+
args[0]->ToString(),
319+
String::NewSymbol(" is not a valid configuration option")
320+
)));
321+
}
322+
323+
return args.This();
324+
}
325+
326+
void Database::RegisterTraceCallback(Baton* baton) {
327+
assert(baton->db->open);
328+
assert(baton->db->handle);
329+
Database* db = baton->db;
330+
331+
if (db->debug_trace == NULL) {
332+
// Add it.
333+
db->debug_trace = new AsyncTrace(db, TraceCallback);
334+
sqlite3_trace(db->handle, TraceCallback, db);
335+
}
336+
else {
337+
// Remove it.
338+
sqlite3_trace(db->handle, NULL, NULL);
339+
delete db->debug_trace;
340+
db->debug_trace = NULL;
341+
}
342+
343+
delete baton;
344+
}
345+
346+
void Database::TraceCallback(void* db, const char* sql) {
347+
// Note: This function is called in the thread pool.
348+
// Note: Some queries, such as "EXPLAIN" queries, are not sent through this.
349+
static_cast<Database*>(db)->debug_trace->send(std::string(sql));
350+
}
351+
352+
void Database::TraceCallback(EV_P_ ev_async *w, int revents) {
353+
// Note: This function is called in the main V8 thread.
354+
HandleScope scope;
355+
AsyncTrace* async = static_cast<AsyncTrace*>(w->data);
356+
357+
std::vector<std::string> queries = async->get();
358+
for (int i = 0; i < queries.size(); i++) {
359+
Local<Value> argv[] = {
360+
String::NewSymbol("trace"),
361+
String::New(queries[i].c_str())
362+
};
363+
EMIT_EVENT(async->parent->handle_, 2, argv);
364+
}
365+
queries.clear();
366+
}
367+
304368
Handle<Value> Database::Exec(const Arguments& args) {
305369
HandleScope scope;
306370
Database* db = ObjectWrap::Unwrap<Database>(args.This());

src/database.h

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,57 @@ class Database : public EventEmitter {
7070
bool exclusive;
7171
Baton* baton;
7272
};
73+
74+
typedef void (*Async_Callback)(EV_P_ ev_async *w, int revents);
75+
76+
77+
// Generic ev_async handler.
78+
template <class Item, class Parent> class Async {
79+
protected:
80+
ev_async watcher;
81+
pthread_mutex_t mutex;
82+
std::vector<Item> data;
83+
public:
84+
Parent* parent;
85+
86+
public:
87+
Async(Parent* parent_, Async_Callback async_cb) : parent(parent_) {
88+
watcher.data = this;
89+
ev_async_init(&watcher, async_cb);
90+
ev_async_start(EV_DEFAULT_UC_ &watcher);
91+
pthread_mutex_init(&mutex, NULL);
92+
}
93+
94+
inline void add(Item item) {
95+
pthread_mutex_lock(&mutex);
96+
data.push_back(item);
97+
pthread_mutex_unlock(&mutex);
98+
}
99+
100+
inline std::vector<Item> get() {
101+
std::vector<Item> rows;
102+
pthread_mutex_lock(&mutex);
103+
rows.swap(data);
104+
pthread_mutex_unlock(&mutex);
105+
return rows;
106+
}
107+
108+
inline void send() {
109+
ev_async_send(EV_DEFAULT_ &watcher);
110+
}
111+
112+
inline void send(Item item) {
113+
add(item);
114+
send();
115+
}
116+
117+
~Async() {
118+
pthread_mutex_destroy(&mutex);
119+
ev_async_stop(EV_DEFAULT_UC_ &watcher);
120+
}
121+
};
122+
123+
typedef Async<std::string, Database> AsyncTrace;
73124

74125
friend class Statement;
75126

@@ -79,12 +130,17 @@ class Database : public EventEmitter {
79130
open(false),
80131
locked(false),
81132
pending(0),
82-
serialize(false) {
133+
serialize(false),
134+
debug_trace(NULL) {
83135

84136
}
85137

86138
~Database() {
87139
assert(handle == NULL);
140+
if (debug_trace) {
141+
delete debug_trace;
142+
debug_trace = NULL;
143+
}
88144
}
89145

90146
static Handle<Value> New(const Arguments& args);
@@ -108,6 +164,11 @@ class Database : public EventEmitter {
108164
static Handle<Value> Serialize(const Arguments& args);
109165
static Handle<Value> Parallelize(const Arguments& args);
110166

167+
static Handle<Value> Configure(const Arguments& args);
168+
static void RegisterTraceCallback(Baton* baton);
169+
static void TraceCallback(void* db, const char* sql);
170+
static void TraceCallback(EV_P_ ev_async *w, int revents);
171+
111172
void Wrap (Handle<Object> handle);
112173
inline void MakeWeak();
113174
virtual void Unref();
@@ -125,6 +186,8 @@ class Database : public EventEmitter {
125186
bool serialize;
126187

127188
std::queue<Call*> queue;
189+
190+
AsyncTrace* debug_trace;
128191
};
129192

130193
}

0 commit comments

Comments
 (0)