DatabaseTopicReader.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5*
6* ArmarX is free software; you can redistribute it and/or modify
7* it under the terms of the GNU General Public License version 2 as
8* published by the Free Software Foundation.
9*
10* ArmarX is distributed in the hope that it will be useful, but
11* WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*
18* @package ArmarX
19* @author Philipp Schmidt( ufedv at student dot kit dot edu)
20* @date 2016
21* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22* GNU General Public License
23*/
24#include "DatabaseTopicReader.h"
25
27
28namespace armarx
29{
30
31 DatabaseTopicReader::DatabaseTopicReader(std::filesystem::path path) :
32 filepath(path), db(nullptr), stmt(nullptr), database_open(true), end_of_database(false)
33 {
34 //Open database
35 int error_code = sqlite3_open(filepath.c_str(), &db);
36 if (error_code != SQLITE_OK)
37 {
38 ARMARX_ERROR_S << "Error opening database: " << sqlite3_errmsg(db);
39 ARMARX_ERROR_S << "Database Path: " << filepath.c_str();
40 sqlite3_close(db);
41 database_open = false;
42 return;
43 }
44
45 //Prepare SQL statement
46 std::string sql_select = "SELECT * FROM TopicData WHERE ID >= ?";
47 error_code = sqlite3_prepare_v2(db, sql_select.c_str(), -1, &stmt, nullptr);
48 if (error_code != SQLITE_OK)
49 {
50 ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
51 ARMARX_ERROR_S << "SQL Query: " << sql_select;
52 sqlite3_finalize(stmt);
53 sqlite3_close(db);
54 database_open = false;
55 return;
56 }
57
58 //Seek to start position
59 if (!seekTo(1))
60 {
61 ARMARX_ERROR_S << "Can not seek to start position, or maybe database is empty.";
62 sqlite3_finalize(stmt);
63 sqlite3_close(db);
64 database_open = false;
65 return;
66 }
67
68 //Check if layout is correct (at least correct amount of columns)
69 if (sqlite3_column_count(stmt) != 5)
70 {
71 ARMARX_ERROR_S << "Database table layout is wrong. Closing database.";
72 sqlite3_finalize(stmt);
73 sqlite3_close(db);
74 database_open = false;
75 return;
76 }
77 }
78
80 {
81 //NULL pointer is a harmless op
82 sqlite3_finalize(stmt);
83 sqlite3_close(db);
84 }
85
86 std::filesystem::path
88 {
89 return filepath;
90 }
91
92 bool
94 {
95 //Make sure this is threadsafe
96 std::unique_lock lock(mutex);
97
98 //Check if database is open
99 if (!database_open)
100 {
101 ARMARX_ERROR_S << "Database not open or database table layout is wrong";
102 return false;
103 }
104
105 //Check if end of select statement is reached
106 if (end_of_database)
107 {
108 //ARMARX_ERROR << "Can not read, end of replay data reached";
109 return false;
110 }
111
112
113 //Return current row
114 data.timestamp = IceUtil::Time::microSeconds(sqlite3_column_int64(stmt, 1));
115 data.topicName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
116 data.operationName =
117 std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3)));
118
119 //Retreive blob data and store in vector
120 const void* blob_data = sqlite3_column_blob(stmt, 4);
121 int size = sqlite3_column_bytes(stmt, 4);
122 //vector range constructor should copy the data according to docu
123 data.inParams = std::vector<Ice::Byte>((Ice::Byte*)blob_data, (Ice::Byte*)blob_data + size);
124
125 //Move to next row for next read access
126 int error_code = sqlite3_step(stmt);
127 //We might get another row or the end of the list
128 if (error_code != SQLITE_ROW)
129 {
130 //Maybe end of select statement?
131 if (error_code == SQLITE_DONE)
132 {
133 ARMARX_INFO << "End of replay reached";
134 end_of_database = true;
135 }
136 else
137 {
138 ARMARX_ERROR_S << "Retrieving next row of data failed, closing database: "
139 << sqlite3_errmsg(db);
140 sqlite3_finalize(stmt);
141 sqlite3_close(db);
142 database_open = false;
143 return false;
144 }
145 }
146
147 return true;
148 }
149
150 bool
152 {
153 sqlite3_stmt* stmt_get_id;
154 std::string sql_get_id = "SELECT ID FROM TopicData WHERE TIME >= ? LIMIT 1";
155
156 int error_code = sqlite3_prepare_v2(db, sql_get_id.c_str(), -1, &stmt_get_id, nullptr);
157 if (error_code != SQLITE_OK)
158 {
159 ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
160 ARMARX_ERROR << "Can not prepare sql statement: " << sqlite3_errmsg(db);
161 ARMARX_ERROR << "SQL Query: " << sql_get_id;
162 sqlite3_finalize(stmt_get_id);
163 return false;
164 }
165
166 error_code = sqlite3_bind_int64(stmt_get_id, 1, timestamp.toMicroSeconds());
167 if (error_code != SQLITE_OK)
168 {
169 ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
170 ARMARX_ERROR << "Can not bind parameter to sql query: " << sqlite3_errmsg(db);
171 ARMARX_ERROR << "SQL Query: " << sql_get_id;
172 sqlite3_finalize(stmt_get_id);
173 return false;
174 }
175
176 error_code = sqlite3_step(stmt_get_id);
177 //Expecting some results, otherwise something went wrong
178 if (error_code != SQLITE_ROW)
179 {
180 ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
181 ARMARX_ERROR << "Query did not succeed or we got no result: " << sqlite3_errmsg(db);
182 ARMARX_ERROR << "SQL Query: " << sql_get_id;
183 sqlite3_finalize(stmt_get_id);
184 return false;
185 }
186
187 bool returnValue = seekTo(sqlite3_column_int(stmt_get_id, 0));
188 sqlite3_finalize(stmt_get_id);
189 return returnValue;
190 }
191
192 IceUtil::Time
194 {
195 // Get timestamp of last entry
196 sqlite3_stmt* stmt_replay_length;
197 std::string sql_replay_length = "SELECT max(TIME) FROM TopicData";
198 int error_code =
199 sqlite3_prepare_v2(db, sql_replay_length.c_str(), -1, &stmt_replay_length, nullptr);
200 if (error_code != SQLITE_OK)
201 {
202 ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
203 ARMARX_ERROR_S << "SQL Query: " << sql_replay_length;
204 sqlite3_finalize(stmt_replay_length);
205 return IceUtil::Time::microSeconds(0);
206 }
207 error_code = sqlite3_step(stmt_replay_length);
208 if (error_code != SQLITE_ROW)
209 {
210 ARMARX_ERROR_S << "Can not execute sql statement, or no result: " << sqlite3_errmsg(db);
211 ARMARX_ERROR_S << "SQL Query: " << sql_replay_length;
212 sqlite3_finalize(stmt_replay_length);
213 return IceUtil::Time::microSeconds(0);
214 }
215
216 long returnValue = sqlite3_column_int64(stmt_replay_length, 0);
217 sqlite3_finalize(stmt_replay_length);
218 return IceUtil::Time::microSeconds(returnValue);
219 }
220
221 std::vector<std::string>
223 {
224 std::vector<std::string> topicList;
225
226 //Check if database is open
227 if (!database_open)
228 {
229 ARMARX_ERROR << "Can not read list of replay topics if database is not open";
230 return topicList;
231 }
232
233 //Retrieve all topics of database
234 sqlite3_stmt* stmt_topic_list;
235 std::string sql_topic_list = "SELECT TOPIC FROM Topics";
236 int error_code =
237 sqlite3_prepare_v2(db, sql_topic_list.c_str(), -1, &stmt_topic_list, nullptr);
238 if (error_code != SQLITE_OK)
239 {
240 ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
241 ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
242 sqlite3_finalize(stmt_topic_list);
243 return topicList;
244 }
245 error_code = sqlite3_step(stmt_topic_list);
246 if (error_code != SQLITE_ROW)
247 {
248 ARMARX_ERROR_S << "Can not execute sql statement, or no result: " << sqlite3_errmsg(db);
249 ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
250 sqlite3_finalize(stmt_topic_list);
251 return topicList;
252 }
253
254 //Process until end of list is reached
255 while (error_code == SQLITE_ROW)
256 {
257 topicList.push_back(std::string(
258 reinterpret_cast<const char*>(sqlite3_column_text(stmt_topic_list, 0))));
259 error_code = sqlite3_step(stmt_topic_list);
260 }
261
262 //Just for good practice
263 //State after reading all columns should be SQLITE_DONE
264 if (error_code != SQLITE_DONE)
265 {
266 ARMARX_ERROR_S << "Error reading topic list: " << sqlite3_errmsg(db);
267 ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
268 sqlite3_finalize(stmt_topic_list);
269 return topicList;
270 }
271
272 sqlite3_finalize(stmt_topic_list);
273 return topicList;
274 }
275
276 bool
278 {
279 //Make sure this is threadsafe
280 std::unique_lock lock(mutex);
281
282 //Check if database is open
283 if (!database_open)
284 {
285 ARMARX_ERROR << "Can not seek to position if database is not open";
286 return false;
287 }
288
289 //Reset statement
290 sqlite3_reset(stmt);
291 sqlite3_clear_bindings(stmt);
292
293 //Reset end of database
294 end_of_database = false;
295
296 //Bind ID
297 int error_code = sqlite3_bind_int(stmt, 1, ID);
298 if (error_code != SQLITE_OK)
299 {
300 ARMARX_ERROR_S << "Could not bind data while trying to seek: " << sqlite3_errmsg(db);
301 sqlite3_finalize(stmt);
302 sqlite3_close(db);
303 database_open = false;
304 return false;
305 }
306
307 error_code = sqlite3_step(stmt);
308 //Expecting some results, otherwise we are at the end of database or something went wrong
309 if (error_code != SQLITE_ROW)
310 {
311 ARMARX_ERROR_S << "Query did not succeed or we got no result while trying to seek: "
312 << sqlite3_errmsg(db);
313 sqlite3_finalize(stmt);
314 sqlite3_close(db);
315 database_open = false;
316 return false;
317 }
318
319 return true;
320 }
321
322} // namespace armarx
std::string timestamp()
bool seekTo(IceUtil::Time timestamp) override
std::filesystem::path getFilepath() const
std::vector< std::string > getReplayTopics() override
DatabaseTopicReader(std::filesystem::path path)
IceUtil::Time getReplayLength() override
bool read(TopicUtil::TopicData &data) override
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
This file offers overloads of toIce() and fromIce() functions for STL container types.