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

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

Function rearrange.

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 if exists
92bool sqlitedbclass::exists (const text_t& key)
93{
94 text_t sql_cmd = "SELECT value FROM data WHERE key='" + key + "'";
95 vector<text_tmap> sql_results;
96 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
97 {
98 return false;
99 }
100
101 return true;
102}
103
104
105// returns true on success
106bool sqlitedbclass::getinfo (const text_t& key, infodbclass &info)
107{
108 text_t sql_cmd = "SELECT value FROM data WHERE key='" + key + "'";
109 vector<text_tmap> sql_results;
110 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
111 {
112 return false;
113 }
114
115 text_tmap sql_result = sql_results[0];
116 text_t sql_result_value = sql_result["value"];
117 text_t::iterator sql_result_value_iterator = sql_result_value.begin();
118 text_t ikey, ivalue;
119 info.clear();
120 while (getinfoline(sql_result_value_iterator, sql_result_value.end(), ikey, ivalue))
121 {
122 info.addinfo(ikey, ivalue);
123 }
124
125 return true;
126}
127
128
129// returns array of keys
130text_tarray sqlitedbclass::getkeys ()
131{
132 text_tarray keys;
133
134 // Get all the entries in the "key" column of the table
135 text_t sql_cmd = "SELECT key FROM data";
136 vector<text_tmap> sql_results;
137 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
138 {
139 return keys;
140 }
141
142 // Iterate through the keys and add them to the array to be returned
143 vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
144 while (sql_results_iterator != sql_results.end())
145 {
146 text_tmap sql_result = (*sql_results_iterator);
147 keys.push_back(sql_result["key"]);
148 sql_results_iterator++;
149 }
150
151 return keys;
152}
153
154
155// returns true on success
156bool sqlitedbclass::setinfo (const text_t &key, const infodbclass &info)
157{
158 text_t subkey;
159 text_t data;
160
161 // get all the keys and values
162 infodbclass::const_iterator info_here = info.begin();
163 infodbclass::const_iterator info_end = info.end();
164 while (info_here != info_end) {
165 // add the key
166 subkey.clear();
167 subkey.push_back('<');
168 text_t::const_iterator subkey_here = (*info_here).first.begin();
169 text_t::const_iterator subkey_end = (*info_here).first.end();
170 while (subkey_here != subkey_end) {
171 if (*subkey_here == '>') {
172 subkey.push_back('\\'); subkey.push_back('>');
173 } else if (*subkey_here == '\n') {
174 subkey.push_back('\\'); subkey.push_back('n');
175 } else if (*subkey_here == '\r') {
176 subkey.push_back('\\'); subkey.push_back('r');
177 } else if (*subkey_here == '\\') {
178 subkey.push_back('\\'); subkey.push_back('\\');
179 } else {
180 subkey.push_back (*subkey_here);
181 }
182 ++subkey_here;
183 }
184 subkey.push_back('>');
185
186 // add the values
187 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
188 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
189 while (subvalue_here != subvalue_end) {
190 data += subkey;
191
192 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
193 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
194 while (thissubvalue_here != thissubvalue_end) {
195 if (*thissubvalue_here == '>') {
196 data.push_back('\\'); data.push_back('>');
197 } else if (*thissubvalue_here == '\n') {
198 data.push_back('\\'); data.push_back('n');
199 } else if (*thissubvalue_here == '\r') {
200 data.push_back('\\'); data.push_back('r');
201 } else if (*thissubvalue_here == '\\') {
202 data.push_back('\\'); data.push_back('\\');
203 } else {
204 data.push_back (*thissubvalue_here);
205 }
206
207 ++thissubvalue_here;
208 }
209
210 data.push_back('\n');
211 ++subvalue_here;
212 }
213
214 ++info_here;
215 }
216
217 // We need to do either an INSERT or UPDATE depending on whether the key already exists
218 if (!exists(key))
219 {
220 text_t sql_cmd = "INSERT INTO data (key, value) VALUES ('" + key + "', '" + data + "')";
221 return sqlexec(sql_cmd);
222 }
223 else
224 {
225 text_t sql_cmd = "UPDATE data SET value='" + data + "' WHERE key='" + key + "'";
226 return sqlexec(sql_cmd);
227 }
228}
229
230
231// returns true on success
232bool sqlitedbclass::getinfoline (text_t::iterator &here, text_t::iterator end,
233 text_t &key, text_t &value)
234{
235 key.clear();
236 value.clear();
237
238 // ignore white space
239 while (here != end && is_unicode_space (*here)) ++here;
240
241 // get the '<'
242 if (here == end || *here != '<') return false;
243 ++here;
244
245 // get the key
246 while (here != end && *here != '>') {
247 key.push_back(*here);
248 ++here;
249 }
250
251 // get the '>'
252 if (here == end || *here != '>') return false;
253 ++here;
254
255 // get the value
256 while (here != end && *here != '\n') {
257 if (*here == '\\') {
258 // found escape character
259 ++here;
260 if (here != end) {
261 if (*here == 'n') value.push_back ('\n');
262 else if (*here == 'r') value.push_back ('\r');
263 else value.push_back(*here);
264 }
265
266 } else {
267 // a normal character
268 value.push_back(*here);
269 }
270
271 ++here;
272 }
273
274 return true;
275}
276
277
278// ----------------------------------------------------------------------------------------
279// CORE SQL FUNCTIONS
280// ----------------------------------------------------------------------------------------
281
282// sqlexec simply executes the given sql statement - it doesn't obtain a
283// result set - returns true if the sql statement was executed successfully
284bool sqlitedbclass::sqlexec(const text_t &sql_cmd)
285{
286 if (sqlitefile == NULL) return false;
287
288 char *sql_cmd_cstr = sql_cmd.getcstr();
289
290 int rv = 0;
291 int tries = 0;
292 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, NULL, NULL, NULL)) == SQLITE_BUSY)
293 {
294 sleep(1000);
295 tries++;
296 if (tries > SQLITE_MAX_RETRIES)
297 {
298 outconvertclass text_t2ascii;
299 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
300 break;
301 }
302 }
303
304 delete[] sql_cmd_cstr;
305
306 if (rv == SQLITE_OK) return true;
307
308 // sqlite3_exec failed - return false
309 outconvertclass text_t2ascii;
310 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
311 return false;
312}
313
314
315// callback functions for sqlgetarray
316static int sqlgetarray_callback(void *res, int numcols, char **vals, char **columnnames)
317{
318 vector<text_tmap> *result = (vector<text_tmap>*) res;
319 text_tmap row;
320
321 for (int i = 0; i < numcols; i++)
322 {
323 row[columnnames[i]] = "";
324 // vals[i] will be NULL if set to a NULL db value
325 if (vals[i])
326 {
327 row[columnnames[i]] = vals[i];
328 }
329 }
330
331 result->push_back(row);
332 return 0;
333}
334
335
336// sqlgetarray executes sql and returns the result set in sql_results
337bool sqlitedbclass::sqlgetarray(const text_t &sql_cmd, vector<text_tmap> &sql_results)
338{
339 if (sqlitefile == NULL) return false;
340
341 char *sql_cmd_cstr = sql_cmd.getcstr();
342 sql_results.erase(sql_results.begin(), sql_results.end());
343
344 int rv = 0;
345 int tries = 0;
346 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, sqlgetarray_callback, &sql_results, NULL)) == SQLITE_BUSY)
347 {
348 sleep(1000);
349 tries++;
350 if (tries > SQLITE_MAX_RETRIES)
351 {
352 outconvertclass text_t2ascii;
353 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
354 break;
355 }
356 }
357
358 delete[] sql_cmd_cstr;
359
360 if (rv == SQLITE_OK) return true;
361
362 // sqlite3_exec failed - return empty result set
363 outconvertclass text_t2ascii;
364 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
365 return false;
366}
367
368
369// returns true if exists
370bool sqlitedbclass::sqltableexists(const text_t &table_name)
371{
372 text_t sql_cmd = "SELECT * FROM sqlite_master WHERE tbl_name='" + table_name + "'";
373 vector<text_tmap> sql_results;
374 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
375 {
376 return false;
377 }
378
379 return true;
380}
381
382
383// sleep for the given number of milliseconds
384void sleep(int m)
385{
386#ifdef __WIN32__
387 Sleep(m);
388#else
389 usleep(m);
390#endif
391}
Note: See TracBrowser for help on using the repository browser.