Mercurial > hg > svcore
comparison rdf/SimpleSPARQLQuery.cpp @ 480:3ffce691c9bf
* Add Redland datastore support to SimpleSPARQLQuery
author | Chris Cannam |
---|---|
date | Thu, 13 Nov 2008 14:23:23 +0000 |
parents | 2019d89ebcf9 |
children | a82645e788fc |
comparison
equal
deleted
inserted
replaced
479:f933062a7f80 | 480:3ffce691c9bf |
---|---|
13 COPYING included with this distribution for more information. | 13 COPYING included with this distribution for more information. |
14 */ | 14 */ |
15 | 15 |
16 #include "SimpleSPARQLQuery.h" | 16 #include "SimpleSPARQLQuery.h" |
17 #include "base/ProgressReporter.h" | 17 #include "base/ProgressReporter.h" |
18 #include "base/Profiler.h" | |
19 | |
20 #include <QMutex> | |
21 #include <QMutexLocker> | |
22 | |
23 #include <set> | |
18 | 24 |
19 #ifdef USE_NEW_RASQAL_API | 25 #ifdef USE_NEW_RASQAL_API |
20 #include <rasqal/rasqal.h> | 26 #include <rasqal/rasqal.h> |
21 #else | 27 #else |
22 #include <rasqal.h> | 28 #include <rasqal.h> |
23 #endif | 29 #endif |
24 | 30 |
31 #ifdef HAVE_REDLAND | |
32 #include <redland.h> | |
33 #endif | |
34 | |
25 //#define DEBUG_SIMPLE_SPARQL_QUERY 1 | 35 //#define DEBUG_SIMPLE_SPARQL_QUERY 1 |
26 | 36 |
27 #include <iostream> | 37 #include <iostream> |
28 | 38 |
29 using std::cerr; | 39 using std::cerr; |
34 { | 44 { |
35 public: | 45 public: |
36 WrasqalWorldWrapper() : m_world(rasqal_new_world()) { } | 46 WrasqalWorldWrapper() : m_world(rasqal_new_world()) { } |
37 ~WrasqalWorldWrapper() { rasqal_free_world(m_world); } | 47 ~WrasqalWorldWrapper() { rasqal_free_world(m_world); } |
38 | 48 |
39 rasqal_world *getWorld() const { return m_world; } | 49 rasqal_world *getWorld() { return m_world; } |
50 const rasqal_world *getWorld() const { return m_world; } | |
40 | 51 |
41 private: | 52 private: |
42 rasqal_world *m_world; | 53 rasqal_world *m_world; |
43 }; | 54 }; |
44 #endif | 55 #endif |
45 | 56 |
57 #ifdef HAVE_REDLAND | |
58 class WredlandWorldWrapper | |
59 { | |
60 public: | |
61 WredlandWorldWrapper() : | |
62 m_world(0), m_storage(0), m_model(0) | |
63 { | |
64 m_world = librdf_new_world(); | |
65 librdf_world_open(m_world); | |
66 m_storage = librdf_new_storage(m_world, NULL, NULL, NULL); | |
67 // m_storage = librdf_new_storage(m_world, "hashes", NULL, | |
68 //. "hash-type='memory',indexes=1"); | |
69 if (!m_storage) { | |
70 std::cerr << "SimpleSPARQLQuery: ERROR: Failed to initialise Redland hashes datastore, falling back to memory store" << std::endl; | |
71 m_storage = librdf_new_storage(m_world, NULL, NULL, NULL); | |
72 if (!m_storage) { | |
73 std::cerr << "SimpleSPARQLQuery: ERROR: Failed to initialise Redland memory datastore" << std::endl; | |
74 return; | |
75 } | |
76 } | |
77 m_model = librdf_new_model(m_world, m_storage, NULL); | |
78 if (!m_model) { | |
79 std::cerr << "SimpleSPARQLQuery: ERROR: Failed to initialise Redland data model" << std::endl; | |
80 return; | |
81 } | |
82 } | |
83 | |
84 ~WredlandWorldWrapper() | |
85 { | |
86 while (!m_parsedUris.empty()) { | |
87 librdf_free_uri(m_parsedUris.begin()->second); | |
88 m_parsedUris.erase(m_parsedUris.begin()); | |
89 } | |
90 if (m_model) librdf_free_model(m_model); | |
91 if (m_storage) librdf_free_storage(m_storage); | |
92 if (m_world) librdf_free_world(m_world); | |
93 } | |
94 | |
95 bool isOK() const { return (m_model != 0); } | |
96 | |
97 librdf_uri *getUri(QString uriString, QString &errorString) | |
98 { | |
99 QMutexLocker locker(&m_mutex); | |
100 | |
101 if (m_parsedUris.find(uriString) != m_parsedUris.end()) { | |
102 return m_parsedUris[uriString]; | |
103 } | |
104 | |
105 librdf_uri *uri = librdf_new_uri | |
106 (m_world, (const unsigned char *)uriString.toUtf8().data()); | |
107 if (!uri) { | |
108 errorString = "Failed to construct librdf_uri!"; | |
109 return 0; | |
110 } | |
111 | |
112 librdf_parser *parser = librdf_new_parser(m_world, "guess", NULL, NULL); | |
113 if (!parser) { | |
114 errorString = "Failed to initialise Redland parser"; | |
115 return 0; | |
116 } | |
117 | |
118 std::cerr << "About to parse \"" << uriString.toStdString() << "\"" << std::endl; | |
119 | |
120 Profiler p("SimpleSPARQLQuery: Parse URI into LIBRDF model"); | |
121 | |
122 if (librdf_parser_parse_into_model(parser, uri, NULL, m_model)) { | |
123 | |
124 errorString = QString("Failed to parse RDF from URI \"%1\"") | |
125 .arg(uriString); | |
126 librdf_free_parser(parser); | |
127 librdf_free_uri(uri); | |
128 return 0; | |
129 | |
130 } else { | |
131 | |
132 librdf_free_parser(parser); | |
133 m_parsedUris[uriString] = uri; | |
134 return uri; | |
135 } | |
136 } | |
137 | |
138 librdf_world *getWorld() { return m_world; } | |
139 const librdf_world *getWorld() const { return m_world; } | |
140 | |
141 librdf_model *getModel() { return m_model; } | |
142 const librdf_model *getModel() const { return m_model; } | |
143 | |
144 private: | |
145 librdf_world *m_world; | |
146 librdf_storage *m_storage; | |
147 librdf_model *m_model; | |
148 | |
149 QMutex m_mutex; | |
150 std::map<QString, librdf_uri *> m_parsedUris; | |
151 }; | |
152 #endif | |
153 | |
46 class SimpleSPARQLQuery::Impl | 154 class SimpleSPARQLQuery::Impl |
47 { | 155 { |
48 public: | 156 public: |
49 Impl(QString query); | 157 Impl(QString fromUri, QString query); |
50 ~Impl(); | 158 ~Impl(); |
51 | 159 |
52 void setProgressReporter(ProgressReporter *reporter) { m_reporter = reporter; } | 160 void setProgressReporter(ProgressReporter *reporter) { m_reporter = reporter; } |
53 bool wasCancelled() const { return m_cancelled; } | 161 bool wasCancelled() const { return m_cancelled; } |
54 | 162 |
55 ResultList execute(); | 163 ResultList execute(); |
56 | 164 |
57 bool isOK() const; | 165 bool isOK() const; |
58 QString getErrorString() const; | 166 QString getErrorString() const; |
59 | 167 |
168 static void setImplementationPreference | |
169 (SimpleSPARQLQuery::ImplementationPreference p) { | |
170 m_preference = p; | |
171 } | |
172 | |
60 protected: | 173 protected: |
61 static void errorHandler(void *, raptor_locator *, const char *); | 174 static void errorHandler(void *, raptor_locator *, const char *); |
62 | 175 |
176 static QMutex m_mutex; | |
177 | |
63 #ifdef USE_NEW_RASQAL_API | 178 #ifdef USE_NEW_RASQAL_API |
64 static WrasqalWorldWrapper m_www; | 179 static WrasqalWorldWrapper *m_rasqal; |
65 #else | 180 #else |
66 static bool m_initialised; | 181 static bool m_rasqalInitialised; |
67 #endif | 182 #endif |
68 | 183 |
184 #ifdef HAVE_REDLAND | |
185 static WredlandWorldWrapper *m_redland; | |
186 #endif | |
187 | |
188 static SimpleSPARQLQuery::ImplementationPreference m_preference; | |
189 | |
190 ResultList executeDirectParser(); | |
191 ResultList executeDatastore(); | |
192 | |
193 QString m_fromUri; | |
69 QString m_query; | 194 QString m_query; |
70 QString m_errorString; | 195 QString m_errorString; |
71 ProgressReporter *m_reporter; | 196 ProgressReporter *m_reporter; |
72 bool m_cancelled; | 197 bool m_cancelled; |
73 }; | 198 }; |
74 | 199 |
75 #ifdef USE_NEW_RASQAL_API | 200 #ifdef USE_NEW_RASQAL_API |
76 WrasqalWorldWrapper | 201 WrasqalWorldWrapper *SimpleSPARQLQuery::Impl::m_rasqal = 0; |
77 SimpleSPARQLQuery::Impl::m_www; | |
78 #else | 202 #else |
79 bool | 203 bool SimpleSPARQLQuery::Impl::m_rasqalInitialised = false; |
80 SimpleSPARQLQuery::Impl::m_initialised = false; | 204 #endif |
81 #endif | 205 |
82 | 206 #ifdef HAVE_REDLAND |
83 SimpleSPARQLQuery::SimpleSPARQLQuery(QString query) : | 207 WredlandWorldWrapper *SimpleSPARQLQuery::Impl::m_redland = 0; |
84 m_impl(new Impl(query)) { } | 208 #endif |
209 | |
210 QMutex SimpleSPARQLQuery::Impl::m_mutex; | |
211 | |
212 SimpleSPARQLQuery::ImplementationPreference | |
213 SimpleSPARQLQuery::Impl::m_preference = SimpleSPARQLQuery::UseDirectParser; | |
214 | |
215 SimpleSPARQLQuery::SimpleSPARQLQuery(QString fromUri, QString query) : | |
216 m_impl(new Impl(fromUri, query)) | |
217 { | |
218 } | |
85 | 219 |
86 SimpleSPARQLQuery::~SimpleSPARQLQuery() | 220 SimpleSPARQLQuery::~SimpleSPARQLQuery() |
87 { | 221 { |
88 delete m_impl; | 222 delete m_impl; |
89 } | 223 } |
116 SimpleSPARQLQuery::getErrorString() const | 250 SimpleSPARQLQuery::getErrorString() const |
117 { | 251 { |
118 return m_impl->getErrorString(); | 252 return m_impl->getErrorString(); |
119 } | 253 } |
120 | 254 |
121 SimpleSPARQLQuery::Impl::Impl(QString query) : | 255 void |
256 SimpleSPARQLQuery::setImplementationPreference(ImplementationPreference p) | |
257 { | |
258 SimpleSPARQLQuery::Impl::setImplementationPreference(p); | |
259 } | |
260 | |
261 SimpleSPARQLQuery::Impl::Impl(QString fromUri, QString query) : | |
262 m_fromUri(fromUri), | |
122 m_query(query), | 263 m_query(query), |
123 m_reporter(0), | 264 m_reporter(0), |
124 m_cancelled(false) | 265 m_cancelled(false) |
125 { | 266 { |
126 #ifdef DEBUG_SIMPLE_SPARQL_QUERY | 267 #ifdef DEBUG_SIMPLE_SPARQL_QUERY |
163 SimpleSPARQLQuery::ResultList | 304 SimpleSPARQLQuery::ResultList |
164 SimpleSPARQLQuery::Impl::execute() | 305 SimpleSPARQLQuery::Impl::execute() |
165 { | 306 { |
166 ResultList list; | 307 ResultList list; |
167 | 308 |
309 ImplementationPreference preference; | |
310 | |
311 m_mutex.lock(); | |
312 | |
313 if (m_preference == UseDatastore) { | |
314 #ifdef HAVE_REDLAND | |
315 if (!m_redland) { | |
316 m_redland = new WredlandWorldWrapper(); | |
317 if (!m_redland->isOK()) { | |
318 cerr << "WARNING: SimpleSPARQLQuery::execute: Failed to initialise Redland datastore, falling back to direct parser implementation" << endl; | |
319 delete m_redland; | |
320 m_preference = UseDirectParser; | |
321 } | |
322 } | |
323 #else | |
324 cerr << "WARNING: SimpleSPARQLQuery::execute: Datastore implementation preference indicated, but no datastore compiled in; using direct parser" << endl; | |
325 m_preference = UseDirectParser; | |
326 #endif | |
327 } | |
328 | |
329 if (m_preference == UseDirectParser) { | |
168 #ifdef USE_NEW_RASQAL_API | 330 #ifdef USE_NEW_RASQAL_API |
169 rasqal_query *query = rasqal_new_query(m_www.getWorld(), "sparql", NULL); | 331 if (!m_rasqal) m_rasqal = new WrasqalWorldWrapper(); |
170 #else | 332 #else |
171 if (!m_initialised) { | 333 if (!m_rasqalInitialised) { |
172 m_initialised = true; | 334 rasqal_init(); |
173 rasqal_init(); | 335 m_rasqalInitialised = true; |
174 } | 336 } |
337 #endif | |
338 } | |
339 | |
340 preference = m_preference; | |
341 m_mutex.unlock(); | |
342 | |
343 if (preference == SimpleSPARQLQuery::UseDirectParser) { | |
344 return executeDirectParser(); | |
345 } else { | |
346 return executeDatastore(); | |
347 } | |
348 } | |
349 | |
350 SimpleSPARQLQuery::ResultList | |
351 SimpleSPARQLQuery::Impl::executeDirectParser() | |
352 { | |
353 ResultList list; | |
354 | |
355 Profiler profiler("SimpleSPARQLQuery::executeDirectParser"); | |
356 | |
357 #ifdef USE_NEW_RASQAL_API | |
358 rasqal_query *query = rasqal_new_query(m_rasqal->getWorld(), "sparql", NULL); | |
359 #else | |
175 rasqal_query *query = rasqal_new_query("sparql", NULL); | 360 rasqal_query *query = rasqal_new_query("sparql", NULL); |
176 #endif | 361 #endif |
177 if (!query) { | 362 if (!query) { |
178 m_errorString = "Failed to construct query"; | 363 m_errorString = "Failed to construct query"; |
179 cerr << "SimpleSPARQLQuery: ERROR: " << m_errorString.toStdString() << endl; | 364 cerr << "SimpleSPARQLQuery: ERROR: " << m_errorString.toStdString() << endl; |
181 } | 366 } |
182 | 367 |
183 rasqal_query_set_error_handler(query, this, errorHandler); | 368 rasqal_query_set_error_handler(query, this, errorHandler); |
184 rasqal_query_set_fatal_error_handler(query, this, errorHandler); | 369 rasqal_query_set_fatal_error_handler(query, this, errorHandler); |
185 | 370 |
186 if (rasqal_query_prepare | 371 { |
187 (query, (const unsigned char *)m_query.toUtf8().data(), NULL)) { | 372 Profiler p("SimpleSPARQLQuery: Prepare RASQAL query"); |
188 cerr << "SimpleSPARQLQuery: Failed to prepare query" << endl; | 373 |
189 rasqal_free_query(query); | 374 if (rasqal_query_prepare |
190 return list; | 375 (query, (const unsigned char *)m_query.toUtf8().data(), NULL)) { |
191 } | 376 cerr << "SimpleSPARQLQuery: Failed to prepare query" << endl; |
192 | 377 rasqal_free_query(query); |
193 rasqal_query_results *results = rasqal_query_execute(query); | 378 return list; |
379 } | |
380 } | |
381 | |
382 rasqal_query_results *results; | |
383 | |
384 { | |
385 Profiler p("SimpleSPARQLQuery: Execute RASQAL query"); | |
386 results = rasqal_query_execute(query); | |
387 } | |
194 | 388 |
195 // cerr << "Query executed" << endl; | 389 // cerr << "Query executed" << endl; |
196 | 390 |
197 if (!results) { | 391 if (!results) { |
198 cerr << "SimpleSPARQLQuery: RASQAL query failed" << endl; | 392 cerr << "SimpleSPARQLQuery: RASQAL query failed" << endl; |
270 rasqal_free_query(query); | 464 rasqal_free_query(query); |
271 | 465 |
272 return list; | 466 return list; |
273 } | 467 } |
274 | 468 |
469 SimpleSPARQLQuery::ResultList | |
470 SimpleSPARQLQuery::Impl::executeDatastore() | |
471 { | |
472 ResultList list; | |
473 #ifndef HAVE_REDLAND | |
474 // This should have been caught by execute() | |
475 cerr << "SimpleSPARQLQuery: INTERNAL ERROR: Datastore not compiled in" << endl; | |
476 return list; | |
477 #else | |
478 Profiler profiler("SimpleSPARQLQuery::executeDatastore"); | |
479 | |
480 librdf_uri *uri = m_redland->getUri(m_fromUri, m_errorString); | |
481 if (!uri) return list; | |
482 | |
483 std::cerr << "SimpleSPARQLQuery: Query is: \"" << m_query.toStdString() << "\"" << std::endl; | |
484 static std::map<QString, int> counter; | |
485 if (counter.find(m_query) == counter.end()) counter[m_query] = 1; | |
486 else ++counter[m_query]; | |
487 std::cerr << "Counter for this query: " << counter[m_query] << std::endl; | |
488 | |
489 librdf_query *query; | |
490 | |
491 { | |
492 Profiler p("SimpleSPARQLQuery: Prepare LIBRDF query"); | |
493 query = librdf_new_query | |
494 (m_redland->getWorld(), "sparql", NULL, | |
495 (const unsigned char *)m_query.toUtf8().data(), uri); | |
496 } | |
497 std::cerr << "Prepared" << std::endl; | |
498 | |
499 if (!query) { | |
500 m_errorString = "Failed to construct query"; | |
501 return list; | |
502 } | |
503 | |
504 librdf_query_results *results; | |
505 { | |
506 Profiler p("SimpleSPARQLQuery: Execute LIBRDF query"); | |
507 results = librdf_query_execute(query, m_redland->getModel()); | |
508 } | |
509 std::cerr << "Executed" << std::endl; | |
510 | |
511 if (!results) { | |
512 cerr << "SimpleSPARQLQuery: LIBRDF query failed" << endl; | |
513 librdf_free_query(query); | |
514 return list; | |
515 } | |
516 | |
517 if (!librdf_query_results_is_bindings(results)) { | |
518 cerr << "SimpleSPARQLQuery: LIBRDF query has wrong result type (not bindings)" << endl; | |
519 librdf_free_query_results(results); | |
520 librdf_free_query(query); | |
521 return list; | |
522 } | |
523 | |
524 int resultCount = 0; | |
525 int resultTotal = librdf_query_results_get_count(results); // probably wrong | |
526 m_cancelled = false; | |
527 | |
528 while (!librdf_query_results_finished(results)) { | |
529 | |
530 int count = librdf_query_results_get_bindings_count(results); | |
531 | |
532 KeyValueMap resultmap; | |
533 | |
534 for (int i = 0; i < count; ++i) { | |
535 | |
536 const char *name = | |
537 librdf_query_results_get_binding_name(results, i); | |
538 | |
539 librdf_node *node = | |
540 librdf_query_results_get_binding_value(results, i); | |
541 | |
542 QString key = (const char *)name; | |
543 | |
544 if (!node) { | |
545 resultmap[key] = Value(); | |
546 continue; | |
547 } | |
548 | |
549 ValueType type = LiteralValue; | |
550 if (librdf_node_is_resource(node)) type = URIValue; | |
551 else if (librdf_node_is_literal(node)) type = LiteralValue; | |
552 else if (librdf_node_is_blank(node)) type = BlankValue; | |
553 else { | |
554 cerr << "SimpleSPARQLQuery: LIBRDF query returned unknown node type (not resource, literal, or blank)" << endl; | |
555 resultmap[key] = Value(); | |
556 librdf_free_node(node); | |
557 continue; | |
558 } | |
559 | |
560 QString text = (const char *)librdf_node_get_literal_value(node); | |
561 | |
562 #ifdef DEBUG_SIMPLE_SPARQL_QUERY | |
563 std::cerr << i << ". " << key.toStdString() << " -> " << text.toStdString() << " (type " << type << ")" << std::endl; | |
564 #endif | |
565 | |
566 resultmap[key] = Value(type, text); | |
567 | |
568 librdf_free_node(node); | |
569 } | |
570 | |
571 list.push_back(resultmap); | |
572 | |
573 librdf_query_results_next(results); | |
574 | |
575 resultCount++; | |
576 | |
577 if (m_reporter) { | |
578 if (resultCount >= resultTotal) { | |
579 if (m_reporter->isDefinite()) m_reporter->setDefinite(false); | |
580 m_reporter->setProgress(resultCount); | |
581 } else { | |
582 m_reporter->setProgress((resultCount * 100) / resultTotal); | |
583 } | |
584 | |
585 if (m_reporter->wasCancelled()) { | |
586 m_cancelled = true; | |
587 break; | |
588 } | |
589 } | |
590 } | |
591 | |
592 librdf_free_query_results(results); | |
593 librdf_free_query(query); | |
594 | |
595 std::cerr << "All results retrieved" << std::endl; | |
596 | |
597 return list; | |
598 #endif | |
599 } | |
600 | |
275 SimpleSPARQLQuery::Value | 601 SimpleSPARQLQuery::Value |
276 SimpleSPARQLQuery::singleResultQuery(QString query, QString binding) | 602 SimpleSPARQLQuery::singleResultQuery(QString fromUri, |
277 { | 603 QString query, QString binding) |
278 SimpleSPARQLQuery q(query); | 604 { |
605 SimpleSPARQLQuery q(fromUri, query); | |
279 ResultList results = q.execute(); | 606 ResultList results = q.execute(); |
280 if (!q.isOK()) { | 607 if (!q.isOK()) { |
281 cerr << "SimpleSPARQLQuery::singleResultQuery: ERROR: " | 608 cerr << "SimpleSPARQLQuery::singleResultQuery: ERROR: " |
282 << q.getErrorString().toStdString() << endl; | 609 << q.getErrorString().toStdString() << endl; |
283 return Value(); | 610 return Value(); |