source: gsdl/trunk/lib/sqlitedbclass.cpp@ 15652

Last change on this file since 15652 was 15652, checked in by mdewsnip, 16 years ago

(Adding new DB support) Moved the exists() function into dbclass because it can be shared between gdbmclass and sqlitedbclass.

File size: 9.8 KB
Line 
1/**********************************************************************
2 *
3 * sqlitedbclass.cpp --
4 * Copyright (C) 2008 DL Consulting Ltd
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "sqlitedbclass.h"
27#include "unitool.h"
28
29
30#define SQLITE_MAX_RETRIES 8
31
32
33sqlitedbclass::~sqlitedbclass()
34{
35 closedatabase();
36}
37
38
39// returns true if opened
40bool sqlitedbclass::opendatabase (const text_t &filename, int mode, int num_retrys,
41#ifdef __WIN32__
42 bool need_filelock
43#else
44 bool
45#endif
46 )
47{
48 // Check if we've already got the database open
49 if (sqlitefile != NULL)
50 {
51 if (openfile == filename) return true;
52 else closedatabase();
53 }
54
55 char *filename_cstr = filename.getcstr();
56 sqlite3_open(filename_cstr, &sqlitefile);
57 delete[] filename_cstr;
58
59 if (sqlitefile == NULL)
60 {
61 (*logout) << "ERROR: sqlitedbclass::opendatabase() failed on: " << filename << "\n";
62 return false;
63 }
64
65 if ((mode == DB_WRITER || mode == DB_WRITER_CREATE) && !sqltableexists("data"))
66 {
67 sqlexec("CREATE TABLE data (key TEXT, value TEXT, PRIMARY KEY(key))");
68 }
69
70 return true;
71}
72
73
74void sqlitedbclass::closedatabase ()
75{
76 if (sqlitefile == NULL) return;
77
78 sqlite3_close(sqlitefile);
79 sqlitefile = NULL;
80 openfile.clear();
81}
82
83
84void sqlitedbclass::deletekey (const text_t &key)
85{
86 text_t sql_cmd = "DELETE FROM data WHERE key='" + key + "'";
87 sqlexec(sql_cmd);
88}
89
90
91// returns true on success
92bool sqlitedbclass::getinfo (const text_t& key, infodbclass &info)
93{
94 text_t data;
95 if (!getkeydata(key, data)) return false;
96
97 // Use getinfoline() to parse the data value into the infodbclass object
98 text_t::iterator data_iterator = data.begin();
99 text_t ikey, ivalue;
100 info.clear();
101 while (getinfoline(data_iterator, data.end(), ikey, ivalue))
102 {
103 info.addinfo(ikey, ivalue);
104 }
105
106 return true;
107}
108
109
110// returns true on success
111bool sqlitedbclass::getkeydata (const text_t& key, text_t &data)
112{
113 text_t sql_cmd = "SELECT value FROM data WHERE key='" + key + "'";
114 vector<text_tmap> sql_results;
115 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
116 {
117 return false;
118 }
119
120 text_tmap sql_result = sql_results[0];
121 data = sql_result["value"];
122 return true;
123}
124
125
126// returns array of keys
127text_tarray sqlitedbclass::getkeys ()
128{
129 text_tarray keys;
130
131 // Get all the entries in the "key" column of the table
132 text_t sql_cmd = "SELECT key FROM data";
133 vector<text_tmap> sql_results;
134 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
135 {
136 return keys;
137 }
138
139 // Iterate through the keys and add them to the array to be returned
140 vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
141 while (sql_results_iterator != sql_results.end())
142 {
143 text_tmap sql_result = (*sql_results_iterator);
144 keys.push_back(sql_result["key"]);
145 sql_results_iterator++;
146 }
147
148 return keys;
149}
150
151
152// returns true on success
153bool sqlitedbclass::setinfo (const text_t &key, const infodbclass &info)
154{
155 text_t subkey;
156 text_t data;
157
158 // get all the keys and values
159 infodbclass::const_iterator info_here = info.begin();
160 infodbclass::const_iterator info_end = info.end();
161 while (info_here != info_end) {
162 // add the key
163 subkey.clear();
164 subkey.push_back('<');
165 text_t::const_iterator subkey_here = (*info_here).first.begin();
166 text_t::const_iterator subkey_end = (*info_here).first.end();
167 while (subkey_here != subkey_end) {
168 if (*subkey_here == '>') {
169 subkey.push_back('\\'); subkey.push_back('>');
170 } else if (*subkey_here == '\n') {
171 subkey.push_back('\\'); subkey.push_back('n');
172 } else if (*subkey_here == '\r') {
173 subkey.push_back('\\'); subkey.push_back('r');
174 } else if (*subkey_here == '\\') {
175 subkey.push_back('\\'); subkey.push_back('\\');
176 } else {
177 subkey.push_back (*subkey_here);
178 }
179 ++subkey_here;
180 }
181 subkey.push_back('>');
182
183 // add the values
184 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
185 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
186 while (subvalue_here != subvalue_end) {
187 data += subkey;
188
189 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
190 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
191 while (thissubvalue_here != thissubvalue_end) {
192 if (*thissubvalue_here == '>') {
193 data.push_back('\\'); data.push_back('>');
194 } else if (*thissubvalue_here == '\n') {
195 data.push_back('\\'); data.push_back('n');
196 } else if (*thissubvalue_here == '\r') {
197 data.push_back('\\'); data.push_back('r');
198 } else if (*thissubvalue_here == '\\') {
199 data.push_back('\\'); data.push_back('\\');
200 } else {
201 data.push_back (*thissubvalue_here);
202 }
203
204 ++thissubvalue_here;
205 }
206
207 data.push_back('\n');
208 ++subvalue_here;
209 }
210
211 ++info_here;
212 }
213
214 return setkeydata(key, data);
215}
216
217
218// returns true on success
219bool sqlitedbclass::setkeydata (const text_t &key, const text_t &data)
220{
221 // We need to do either an INSERT or UPDATE depending on whether the key already exists
222 if (!exists(key))
223 {
224 text_t sql_cmd = "INSERT INTO data (key, value) VALUES ('" + key + "', '" + data + "')";
225 return sqlexec(sql_cmd);
226 }
227 else
228 {
229 text_t sql_cmd = "UPDATE data SET value='" + data + "' WHERE key='" + key + "'";
230 return sqlexec(sql_cmd);
231 }
232}
233
234
235// returns true on success
236bool sqlitedbclass::getinfoline (text_t::iterator &here, text_t::iterator end,
237 text_t &key, text_t &value)
238{
239 key.clear();
240 value.clear();
241
242 // ignore white space
243 while (here != end && is_unicode_space (*here)) ++here;
244
245 // get the '<'
246 if (here == end || *here != '<') return false;
247 ++here;
248
249 // get the key
250 while (here != end && *here != '>') {
251 key.push_back(*here);
252 ++here;
253 }
254
255 // get the '>'
256 if (here == end || *here != '>') return false;
257 ++here;
258
259 // get the value
260 while (here != end && *here != '\n') {
261 if (*here == '\\') {
262 // found escape character
263 ++here;
264 if (here != end) {
265 if (*here == 'n') value.push_back ('\n');
266 else if (*here == 'r') value.push_back ('\r');
267 else value.push_back(*here);
268 }
269
270 } else {
271 // a normal character
272 value.push_back(*here);
273 }
274
275 ++here;
276 }
277
278 return true;
279}
280
281
282// ----------------------------------------------------------------------------------------
283// CORE SQL FUNCTIONS
284// ----------------------------------------------------------------------------------------
285
286// sqlexec simply executes the given sql statement - it doesn't obtain a
287// result set - returns true if the sql statement was executed successfully
288bool sqlitedbclass::sqlexec(const text_t &sql_cmd)
289{
290 if (sqlitefile == NULL) return false;
291
292 char *sql_cmd_cstr = sql_cmd.getcstr();
293
294 int rv = 0;
295 int tries = 0;
296 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, NULL, NULL, NULL)) == SQLITE_BUSY)
297 {
298 sleep(1000);
299 tries++;
300 if (tries > SQLITE_MAX_RETRIES)
301 {
302 outconvertclass text_t2ascii;
303 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
304 break;
305 }
306 }
307
308 delete[] sql_cmd_cstr;
309
310 if (rv == SQLITE_OK) return true;
311
312 // sqlite3_exec failed - return false
313 outconvertclass text_t2ascii;
314 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
315 return false;
316}
317
318
319// callback functions for sqlgetarray
320static int sqlgetarray_callback(void *res, int numcols, char **vals, char **columnnames)
321{
322 vector<text_tmap> *result = (vector<text_tmap>*) res;
323 text_tmap row;
324
325 for (int i = 0; i < numcols; i++)
326 {
327 row[columnnames[i]] = "";
328 // vals[i] will be NULL if set to a NULL db value
329 if (vals[i])
330 {
331 row[columnnames[i]] = vals[i];
332 }
333 }
334
335 result->push_back(row);
336 return 0;
337}
338
339
340// sqlgetarray executes sql and returns the result set in sql_results
341bool sqlitedbclass::sqlgetarray(const text_t &sql_cmd, vector<text_tmap> &sql_results)
342{
343 if (sqlitefile == NULL) return false;
344
345 char *sql_cmd_cstr = sql_cmd.getcstr();
346 sql_results.erase(sql_results.begin(), sql_results.end());
347
348 int rv = 0;
349 int tries = 0;
350 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, sqlgetarray_callback, &sql_results, NULL)) == SQLITE_BUSY)
351 {
352 sleep(1000);
353 tries++;
354 if (tries > SQLITE_MAX_RETRIES)
355 {
356 outconvertclass text_t2ascii;
357 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
358 break;
359 }
360 }
361
362 delete[] sql_cmd_cstr;
363
364 if (rv == SQLITE_OK) return true;
365
366 // sqlite3_exec failed - return empty result set
367 outconvertclass text_t2ascii;
368 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
369 return false;
370}
371
372
373// returns true if exists
374bool sqlitedbclass::sqltableexists(const text_t &table_name)
375{
376 text_t sql_cmd = "SELECT * FROM sqlite_master WHERE tbl_name='" + table_name + "'";
377 vector<text_tmap> sql_results;
378 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
379 {
380 return false;
381 }
382
383 return true;
384}
385
386
387// sleep for the given number of milliseconds
388void sleep(int m)
389{
390#ifdef __WIN32__
391 Sleep(m);
392#else
393 usleep(m);
394#endif
395}
Note: See TracBrowser for help on using the repository browser.