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

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

(Adding dynamic classifiers) Not sure if this is necessarily the best place to do this, but added the ability to request the documents from a "get_documents_with_metadata_value()" call to be sorted based on a certain metadata element. Thanks to John Thompson for the SQL statement help.

File size: 9.4 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 "gsdlunicode.h"
28#include "unitool.h"
29
30#ifdef __WIN32__
31// for Sleep
32# include <windows.h>
33#else
34// for usleep
35# include <unistd.h>
36#endif
37
38
39#define SQLITE_MAX_RETRIES 8
40
41
42sqlitedbclass::~sqlitedbclass()
43{
44 closedatabase();
45}
46
47
48// returns true if opened
49bool sqlitedbclass::opendatabase (const text_t &filename, int mode, int num_retrys,
50#ifdef __WIN32__
51 bool need_filelock
52#else
53 bool
54#endif
55 )
56{
57 // Check if we've already got the database open
58 if (sqlitefile != NULL)
59 {
60 if (openfile == filename) return true;
61 else closedatabase();
62 }
63
64 char *filename_cstr = filename.getcstr();
65 sqlite3_open(filename_cstr, &sqlitefile);
66 delete[] filename_cstr;
67
68 if (sqlitefile == NULL)
69 {
70 (*logout) << "ERROR: sqlitedbclass::opendatabase() failed on: " << filename << "\n";
71 return false;
72 }
73
74 if ((mode == DB_WRITER || mode == DB_WRITER_CREATE) && !sqltableexists("data"))
75 {
76 sqlexec("CREATE TABLE data (key TEXT, value TEXT, PRIMARY KEY(key))");
77 }
78
79 return true;
80}
81
82
83void sqlitedbclass::closedatabase ()
84{
85 if (sqlitefile == NULL) return;
86
87 sqlite3_close(sqlitefile);
88 sqlitefile = NULL;
89 openfile.clear();
90}
91
92
93void sqlitedbclass::deletekey (const text_t &key)
94{
95 text_t sql_cmd = "DELETE FROM data WHERE key='" + key + "'";
96 sqlexec(sql_cmd);
97}
98
99
100// returns array of document OIDs
101text_tarray sqlitedbclass::get_documents_with_metadata_value (const text_t &metadata_element_name,
102 const text_t &metadata_value,
103 const text_t &sort_by_metadata_element_name)
104{
105 text_tarray document_OIDs;
106
107 // Check a metadata element and value has been specified
108 if (metadata_element_name == "" || metadata_value == "")
109 {
110 return document_OIDs;
111 }
112
113 // Get the entries in the "document_metadata" table where the element and value matches those specified
114 text_t sql_cmd;
115 if (sort_by_metadata_element_name == "")
116 {
117 // No sorting required
118 sql_cmd = "SELECT docOID FROM document_metadata WHERE element='" + metadata_element_name + "' AND value='" + metadata_value + "'";
119 }
120 else
121 {
122 // Sort the documents by a certain metadata element
123 // John Thompson thinks this may not be the most efficient solution, and recommends using ON instead of WHERE
124 sql_cmd = "SELECT b.docOID FROM document_metadata AS a LEFT JOIN document_metadata AS b USING (docOID) WHERE a.element='" + metadata_element_name + "' AND a.value='" + metadata_value + "' AND b.element='" + sort_by_metadata_element_name + "' ORDER BY b.value";
125 }
126 vector<text_tmap> sql_results;
127 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
128 {
129 return document_OIDs;
130 }
131
132 // Iterate through the documents and add them to the array to be returned
133 vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
134 while (sql_results_iterator != sql_results.end())
135 {
136 text_tmap sql_result = (*sql_results_iterator);
137 document_OIDs.push_back(sql_result["docOID"]);
138 sql_results_iterator++;
139 }
140
141 return document_OIDs;
142}
143
144
145// returns file extension string
146text_t sqlitedbclass::getfileextension ()
147{
148 return ".db";
149}
150
151
152// returns true on success
153bool sqlitedbclass::getkeydata (const text_t& key, text_t &data)
154{
155 text_t sql_cmd = "SELECT value FROM data WHERE key='" + key + "'";
156 vector<text_tmap> sql_results;
157 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
158 {
159 return false;
160 }
161
162 text_tmap sql_result = sql_results[0];
163 data = sql_result["value"];
164 return true;
165}
166
167
168// returns array of keys
169text_tarray sqlitedbclass::getkeys ()
170{
171 text_tarray keys;
172
173 // Get all the entries in the "key" column of the table
174 text_t sql_cmd = "SELECT key FROM data";
175 vector<text_tmap> sql_results;
176 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
177 {
178 return keys;
179 }
180
181 // Iterate through the keys and add them to the array to be returned
182 vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
183 while (sql_results_iterator != sql_results.end())
184 {
185 text_tmap sql_result = (*sql_results_iterator);
186 keys.push_back(sql_result["key"]);
187 sql_results_iterator++;
188 }
189
190 return keys;
191}
192
193
194// returns array of values
195text_tarray sqlitedbclass::get_metadata_values (const text_t &metadata_element_name)
196{
197 text_tarray metadata_values;
198
199 // Check a metadata element has been specified
200 if (metadata_element_name == "")
201 {
202 return metadata_values;
203 }
204
205 // Get the entries in the "document_metadata" table where the element matches that specified
206 text_t sql_cmd = "SELECT value FROM document_metadata WHERE element='" + metadata_element_name + "'";
207 vector<text_tmap> sql_results;
208 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
209 {
210 return metadata_values;
211 }
212
213 // Iterate through the values and add them to the array to be returned
214 vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
215 while (sql_results_iterator != sql_results.end())
216 {
217 text_tmap sql_result = (*sql_results_iterator);
218 metadata_values.push_back(sql_result["value"]);
219 sql_results_iterator++;
220 }
221
222 return metadata_values;
223}
224
225
226// returns true on success
227bool sqlitedbclass::setkeydata (const text_t &key, const text_t &data)
228{
229 // We need to do either an INSERT or UPDATE depending on whether the key already exists
230 if (!exists(key))
231 {
232 text_t sql_cmd = "INSERT INTO data (key, value) VALUES ('" + key + "', '" + data + "')";
233 return sqlexec(sql_cmd);
234 }
235 else
236 {
237 text_t sql_cmd = "UPDATE data SET value='" + data + "' WHERE key='" + key + "'";
238 return sqlexec(sql_cmd);
239 }
240}
241
242
243// ----------------------------------------------------------------------------------------
244// SQLITE-ONLY FUNCTIONS
245// ----------------------------------------------------------------------------------------
246
247// sleep for the given number of milliseconds
248void sleep(int m)
249{
250#ifdef __WIN32__
251 Sleep(m);
252#else
253 usleep(m);
254#endif
255}
256
257
258// sqlexec simply executes the given sql statement - it doesn't obtain a
259// result set - returns true if the sql statement was executed successfully
260bool sqlitedbclass::sqlexec(const text_t &sql_cmd)
261{
262 if (sqlitefile == NULL) return false;
263
264 char *sql_cmd_cstr = sql_cmd.getcstr();
265
266 int rv = 0;
267 int tries = 0;
268 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, NULL, NULL, NULL)) == SQLITE_BUSY)
269 {
270 sleep(1000);
271 tries++;
272 if (tries > SQLITE_MAX_RETRIES)
273 {
274 outconvertclass text_t2ascii;
275 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
276 break;
277 }
278 }
279
280 delete[] sql_cmd_cstr;
281
282 if (rv == SQLITE_OK) return true;
283
284 // sqlite3_exec failed - return false
285 outconvertclass text_t2ascii;
286 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
287 return false;
288}
289
290
291// callback functions for sqlgetarray
292static int sqlgetarray_callback(void *res, int numcols, char **vals, char **columnnames)
293{
294 vector<text_tmap> *result = (vector<text_tmap>*) res;
295 text_tmap row;
296
297 for (int i = 0; i < numcols; i++)
298 {
299 row[columnnames[i]] = "";
300 // vals[i] will be NULL if set to a NULL db value
301 if (vals[i])
302 {
303 row[columnnames[i]] = to_uni(vals[i]);
304 }
305 }
306
307 result->push_back(row);
308 return 0;
309}
310
311
312// sqlgetarray executes sql and returns the result set in sql_results
313bool sqlitedbclass::sqlgetarray(const text_t &sql_cmd, vector<text_tmap> &sql_results)
314{
315 if (sqlitefile == NULL) return false;
316
317 char *sql_cmd_cstr = sql_cmd.getcstr();
318 sql_results.erase(sql_results.begin(), sql_results.end());
319
320 int rv = 0;
321 int tries = 0;
322 while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, sqlgetarray_callback, &sql_results, NULL)) == SQLITE_BUSY)
323 {
324 sleep(1000);
325 tries++;
326 if (tries > SQLITE_MAX_RETRIES)
327 {
328 outconvertclass text_t2ascii;
329 (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
330 break;
331 }
332 }
333
334 delete[] sql_cmd_cstr;
335
336 if (rv == SQLITE_OK) return true;
337
338 // sqlite3_exec failed - return empty result set
339 outconvertclass text_t2ascii;
340 (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
341 return false;
342}
343
344
345// returns true if exists
346bool sqlitedbclass::sqltableexists(const text_t &table_name)
347{
348 text_t sql_cmd = "SELECT * FROM sqlite_master WHERE tbl_name='" + table_name + "'";
349 vector<text_tmap> sql_results;
350 if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
351 {
352 return false;
353 }
354
355 return true;
356}
Note: See TracBrowser for help on using the repository browser.