Skip to content

Commit 6166bfb

Browse files
committed
Added database layer
1 parent d184835 commit 6166bfb

17 files changed

Lines changed: 498 additions & 40 deletions

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# snowplow-cpp-tracker
2-
Snowplow event tracker for C++ __not__ using Boost. Add analytics to your C++ applications, servers and games
2+
Snowplow event tracker for C++. Add analytics to your C++ applications, servers and games
33

44
```bash
5-
g++ -std=c++11 src/*.cpp src/test/*.cpp -o build/test && ./build/test
5+
## OSX
6+
g++ \
7+
-std=c++11 \
8+
-l sqlite3 \
9+
src/vendored/*.cpp \
10+
src/*.cpp \
11+
src/test/*.cpp \
12+
-o build/test && \
13+
./build/test
614
```
715

816
General layout:

src/event_row.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright (c) 2016 Snowplow Analytics Ltd. All rights reserved.
3+
4+
This program is licensed to you under the Apache License Version 2.0,
5+
and you may not use this file except in compliance with the Apache License Version 2.0.
6+
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
8+
Unless required by applicable law or agreed to in writing,
9+
software distributed under the Apache License Version 2.0 is distributed on an
10+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
#include "event_row.hpp"
15+
16+
EventRow::EventRow(int id, Payload event) {
17+
this->m_id = id;
18+
this->m_event = event;
19+
}
20+
21+
int EventRow::get_id() {
22+
return this->m_id;
23+
}
24+
25+
Payload EventRow::get_event() {
26+
return this->m_event;
27+
}

src/event_row.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright (c) 2016 Snowplow Analytics Ltd. All rights reserved.
3+
4+
This program is licensed to you under the Apache License Version 2.0,
5+
and you may not use this file except in compliance with the Apache License Version 2.0.
6+
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
8+
Unless required by applicable law or agreed to in writing,
9+
software distributed under the Apache License Version 2.0 is distributed on an
10+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
#ifndef EVENT_ROW_H
15+
#define EVENT_ROW_H
16+
17+
#include <string>
18+
#include "Payload.hpp"
19+
20+
using namespace std;
21+
22+
class EventRow {
23+
private:
24+
int m_id;
25+
Payload m_event;
26+
27+
public:
28+
EventRow(int id, Payload event);
29+
int get_id();
30+
Payload get_event();
31+
};
32+
33+
#endif

src/http_request_result.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,23 @@ HttpRequestResult::HttpRequestResult(int error_code, int http_return_code) {
1818
this->http_response_code = -1;
1919
this->internal_error_code = error_code;
2020
this->is_successful = false;
21-
}
22-
else {
21+
} else {
2322
this->http_response_code = http_return_code;
2423

2524
if (http_response_code == 200) {
2625
this->is_successful = true;
2726
} else {
28-
this->is_successful = false;
27+
this->is_successful = false;
2928
}
3029

3130
this->internal_error_code = 0;
3231
}
3332
}
3433

35-
int HttpRequestResult::get_http_response_code()
36-
{
34+
int HttpRequestResult::get_http_response_code() {
3735
return this->http_response_code;
3836
}
3937

40-
bool HttpRequestResult::is_success()
41-
{
38+
bool HttpRequestResult::is_success() {
4239
return this->is_successful;
4340
}

src/http_request_result.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,19 @@ software distributed under the Apache License Version 2.0 is distributed on an
1111
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
1212
*/
1313

14-
#pragma once
14+
#ifndef HTTP_REQUEST_RESULT_H
15+
#define HTTP_REQUEST_RESULT_H
1516

1617
class HttpRequestResult {
18+
private:
1719
int http_response_code;
1820
int internal_error_code;
1921
bool is_successful;
22+
2023
public:
2124
HttpRequestResult(int, int);
2225
int get_http_response_code();
2326
bool is_success();
2427
};
28+
29+
#endif

src/payload.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,36 @@ See the Apache License Version 2.0 for the specific language governing permissio
1313

1414
#include "payload.hpp"
1515

16+
Payload::~Payload() {
17+
this->m_pairs.clear();
18+
}
19+
1620
void Payload::add(string key, string value) {
1721
if (!key.empty() && !value.empty()) {
18-
m_pairs[key] = value;
22+
this->m_pairs[key] = value;
1923
}
2024
}
2125

2226
void Payload::add_map(map<string, string> pairs) {
2327
map<string, string>::iterator it;
2428
for (it = pairs.begin(); it != pairs.end(); it++) {
25-
add(it->first, it->second);
29+
this->add(it->first, it->second);
2630
}
2731
}
2832

2933
void Payload::add_payload(Payload p) {
30-
add_map(p.get());
34+
this->add_map(p.get());
3135
}
3236

3337
void Payload::add_json(json j, bool base64Encode, string encoded, string not_encoded) {
3438
if (base64Encode) {
3539
string json_str = j.dump();
36-
add(encoded, base64_encode((const unsigned char *) json_str.c_str(), json_str.length()));
40+
this->add(encoded, base64_encode((const unsigned char *) json_str.c_str(), json_str.length()));
3741
} else {
38-
add(not_encoded, j.dump());
42+
this->add(not_encoded, j.dump());
3943
}
4044
}
45+
46+
map<string, string> Payload::get() {
47+
return this->m_pairs;
48+
}

src/payload.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,20 @@ See the Apache License Version 2.0 for the specific language governing permissio
1919
#include "vendored/json.hpp"
2020
#include "vendored/base64.hpp"
2121

22-
using json = nlohmann::json;
2322
using namespace std;
23+
using json = nlohmann::json;
2424

2525
class Payload {
2626
private:
2727
map<string, string> m_pairs;
2828

2929
public:
30+
~Payload();
3031
void add(string key, string value);
3132
void add_map(map<string, string> pairs);
3233
void add_payload(Payload p);
3334
void add_json(json j, bool base64Encode, string encoded, string not_encoded);
34-
map<string, string> get() { return m_pairs; }
35+
map<string, string> get();
3536
};
3637

3738
#endif

src/storage.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
Copyright (c) 2016 Snowplow Analytics Ltd. All rights reserved.
3+
4+
This program is licensed to you under the Apache License Version 2.0,
5+
and you may not use this file except in compliance with the Apache License Version 2.0.
6+
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
8+
Unless required by applicable law or agreed to in writing,
9+
software distributed under the Apache License Version 2.0 is distributed on an
10+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
14+
#include "storage.hpp"
15+
16+
const string db_table_name = "events";
17+
const string db_column_id = "id";
18+
const string db_column_event = "event";
19+
20+
Storage::Storage(string db_name) {
21+
sqlite3 *db;
22+
char *err_msg = 0;
23+
int rc;
24+
25+
// Open Database connection
26+
rc = sqlite3_open((const char *) db_name.c_str(), &db);
27+
if (rc) {
28+
cerr << "FATAL: Cannot open database: " << sqlite3_errmsg(db) << endl;
29+
exit(1);
30+
}
31+
this->m_db_name = db_name;
32+
this->m_db = db;
33+
34+
// WAL query
35+
string wal_query = "PRAGMA journal_mode=WAL;";
36+
rc = sqlite3_exec(this->m_db, (const char *) wal_query.c_str(), NULL, NULL, &err_msg);
37+
if (rc != SQLITE_OK) {
38+
cerr << "FATAL: Cannot enable WAL: " << err_msg << endl;
39+
sqlite3_free(err_msg);
40+
exit(1);
41+
}
42+
43+
// Create query
44+
string create_query =
45+
"CREATE TABLE IF NOT EXISTS " + db_table_name + "(" +
46+
db_column_id + " INTEGER PRIMARY KEY, " +
47+
db_column_event + " STRING" +
48+
");";
49+
50+
// Make new events table
51+
rc = sqlite3_exec(this->m_db, (const char *) create_query.c_str(), NULL, NULL, &err_msg);
52+
if (rc != SQLITE_OK) {
53+
cerr << "FATAL: Cannot create events table: " << err_msg << endl;
54+
sqlite3_free(err_msg);
55+
exit(1);
56+
}
57+
58+
// Insert query
59+
string insert_query =
60+
"INSERT INTO " + db_table_name + "(" +
61+
db_column_event +
62+
") values(?1);";
63+
64+
// Prepare insert statement
65+
rc = sqlite3_prepare_v2(this->m_db, (const char *) insert_query.c_str(), -1, &this->m_add_stmt, NULL);
66+
if (rc != SQLITE_OK) {
67+
cerr << "FATAL: Cannot prepare event insert statement: " << rc << endl;
68+
exit(1);
69+
}
70+
}
71+
72+
Storage::~Storage() {
73+
sqlite3_finalize(this->m_add_stmt);
74+
sqlite3_close(this->m_db);
75+
}
76+
77+
// --- INSERT
78+
79+
void Storage::insert_payload(Payload payload) {
80+
int rc;
81+
82+
string payload_str = Utils::serialize_payload(payload);
83+
84+
rc = sqlite3_bind_text(this->m_add_stmt, 1, payload_str.c_str(), payload_str.length(), SQLITE_STATIC);
85+
if (rc != SQLITE_OK) {
86+
cerr << "ERROR: Failed to bind payload to statement: " << rc << endl;
87+
return;
88+
}
89+
90+
rc = sqlite3_step(this->m_add_stmt);
91+
if (rc != SQLITE_DONE) {
92+
cerr << "ERROR: Failed to execute add_stmt: " << rc << endl;
93+
}
94+
95+
rc = sqlite3_reset(this->m_add_stmt);
96+
if (rc != SQLITE_OK) {
97+
cerr << "ERROR: Failed to reset add_stmt after insert: " << rc << endl;
98+
}
99+
}
100+
101+
// --- SELECT
102+
103+
static int select_callback(void *data, int argc, char **argv, char **az_col_name) {
104+
int i, id;
105+
list<EventRow>* data_list = (list<EventRow>*)data;
106+
Payload event;
107+
108+
for (i = 0; i < argc; i++) {
109+
if (az_col_name[i] == db_column_id) {
110+
id = std::stoi(argv[i] ? argv[i] : "-1");
111+
} else if (az_col_name[i] == db_column_event) {
112+
event = Utils::deserialize_json_str(argv[i] ? argv[i] : "");
113+
}
114+
}
115+
116+
EventRow event_row(id, event);
117+
data_list->push_back(event_row);
118+
return 0;
119+
}
120+
121+
void Storage::select_all_event_rows(list<EventRow>* event_list) {
122+
int rc;
123+
char *err_msg = 0;
124+
125+
string select_all_query =
126+
"SELECT * FROM " + db_table_name + ";";
127+
128+
rc = sqlite3_exec(this->m_db, (const char *) select_all_query.c_str(), select_callback, (void*)event_list, &err_msg);
129+
if (rc != SQLITE_OK) {
130+
cerr << "ERROR: Failed to execute select_all_query: " << rc << "; " << err_msg << endl;
131+
sqlite3_free(err_msg);
132+
}
133+
}
134+
135+
void Storage::select_event_row_range(list<EventRow>* event_list, int range) {
136+
int rc;
137+
char *err_msg = 0;
138+
139+
string select_range_query =
140+
"SELECT * FROM " + db_table_name + " " +
141+
"ORDER BY " + db_column_id + " ASC LIMIT " + std::to_string(range) + ";";
142+
143+
rc = sqlite3_exec(this->m_db, (const char *) select_range_query.c_str(), select_callback, (void*)event_list, &err_msg);
144+
if (rc != SQLITE_OK) {
145+
cerr << "ERROR: Failed to execute select_range_query: " << rc << "; " << err_msg << endl;
146+
sqlite3_free(err_msg);
147+
}
148+
}
149+
150+
// --- DELETE
151+
152+
void Storage::delete_all_event_rows() {
153+
int rc;
154+
char *err_msg = 0;
155+
156+
string delete_all_query =
157+
"DELETE FROM " + db_table_name + ";";
158+
159+
rc = sqlite3_exec(this->m_db, (const char *) delete_all_query.c_str(), NULL, NULL, &err_msg);
160+
if (rc != SQLITE_OK) {
161+
cerr << "ERROR: Failed to execute delete_all_query: " << rc << "; " << err_msg << endl;
162+
sqlite3_free(err_msg);
163+
}
164+
}
165+
166+
void Storage::delete_event_row_ids(list<int>* id_list) {
167+
int rc;
168+
char *err_msg = 0;
169+
170+
string delete_range_query =
171+
"DELETE FROM " + db_table_name + " " +
172+
"WHERE " + db_column_id + " in(" + Utils::int_list_to_string(id_list, ",") + ");";
173+
174+
rc = sqlite3_exec(this->m_db, (const char *) delete_range_query.c_str(), NULL, NULL, &err_msg);
175+
if (rc != SQLITE_OK) {
176+
cerr << "ERROR: Failed to execute delete_range_query: " << rc << "; " << err_msg << endl;
177+
sqlite3_free(err_msg);
178+
}
179+
}
180+
181+
// --- Getters
182+
183+
string Storage::get_db_name() {
184+
return this->m_db_name;
185+
}

0 commit comments

Comments
 (0)