Allow no element set name again
[mp-sparql-moved-to-github.git] / src / filter_sparql.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include <metaproxy/package.hpp>
20 #include <metaproxy/util.hpp>
21 #include <yaz/log.h>
22 #include <yaz/srw.h>
23 #include <yaz/diagbib1.h>
24 #include <yaz/match_glob.h>
25 #include <boost/scoped_ptr.hpp>
26 #include <boost/thread/mutex.hpp>
27 #include <boost/thread/condition.hpp>
28 #include "sparql.h"
29
30 #include <yaz/zgdu.h>
31
32 namespace mp = metaproxy_1;
33 namespace yf = mp::filter;
34
35 namespace metaproxy_1 {
36     namespace filter {
37         class SPARQL : public Base {
38             class Session;
39             class Rep;
40             class Conf;
41             class Result;
42             class FrontendSet;
43
44             typedef boost::shared_ptr<Session> SessionPtr;
45             typedef boost::shared_ptr<Conf> ConfPtr;
46
47             typedef boost::shared_ptr<FrontendSet> FrontendSetPtr;
48             typedef std::map<std::string,FrontendSetPtr> FrontendSets;
49         public:
50             SPARQL();
51             ~SPARQL();
52             void process(metaproxy_1::Package & package) const;
53             void configure(const xmlNode * ptr, bool test_only,
54                            const char *path);
55             SessionPtr get_session(Package &package, Z_APDU **apdu) const;
56             void release_session(Package &package) const;
57             boost::scoped_ptr<Rep> m_p;
58             std::list<ConfPtr> db_conf;
59         };
60         class SPARQL::Conf {
61         public:
62             std::string db;
63             std::string uri;
64             std::string schema;
65             yaz_sparql_t s;
66             ~Conf();
67         };
68         class SPARQL::Rep {
69             friend class SPARQL;
70             boost::condition m_cond_session_ready;
71             boost::mutex m_mutex;
72             std::map<mp::Session,SessionPtr> m_clients;
73         };
74         class SPARQL::Result {
75         public:
76             Result();
77             ~Result();
78         private:
79             friend class FrontendSet;
80             friend class Session;
81             ConfPtr conf;
82             xmlDoc *doc;
83         };
84         class SPARQL::FrontendSet {
85         private:
86             friend class Session;
87             Odr_int hits;
88             std::string db;
89             std::list<Result> results;
90         };
91         class SPARQL::Session {
92         public:
93             Session(const SPARQL *);
94             ~Session();
95             void handle_z(Package &package, Z_APDU *apdu);
96             Z_APDU *search(mp::Package &package,
97                            Z_APDU *apdu_req,
98                            mp::odr &odr,
99                            const char *sparql_query,
100                            ConfPtr conf, FrontendSetPtr fset);
101             int invoke_sparql(mp::Package &package,
102                               const char *sparql_query,
103                               ConfPtr conf,
104                               WRBUF w);
105
106             Z_Records *fetch(
107                 Package &package,
108                 FrontendSetPtr fset,
109                 ODR odr, Odr_oid *preferredRecordSyntax,
110                 Z_ElementSetNames *esn,
111                 int start, int number, int &error_code, std::string &addinfo,
112                 int *number_returned, int *next_position);
113             bool m_in_use;
114         private:
115             bool m_support_named_result_sets;
116             FrontendSets m_frontend_sets;
117             const SPARQL *m_sparql;
118         };
119     }
120 }
121
122 yf::SPARQL::Result::~Result()
123 {
124     if (doc)
125         xmlFreeDoc(doc);
126 }
127
128 yf::SPARQL::Result::Result()
129 {
130     doc = 0;
131 }
132
133 yf::SPARQL::SPARQL() : m_p(new Rep)
134 {
135 }
136
137 yf::SPARQL::~SPARQL()
138 {
139 }
140
141 void yf::SPARQL::configure(const xmlNode *xmlnode, bool test_only,
142                            const char *path)
143 {
144     const xmlNode *ptr = xmlnode->children;
145     std::string uri;
146
147     for (; ptr; ptr = ptr->next)
148     {
149         if (ptr->type != XML_ELEMENT_NODE)
150             continue;
151         if (!strcmp((const char *) ptr->name, "defaults"))
152         {
153             const struct _xmlAttr *attr;
154             for (attr = ptr->properties; attr; attr = attr->next)
155             {
156                 if (!strcmp((const char *) attr->name, "uri"))
157                     uri = mp::xml::get_text(attr->children);
158                 else
159                     throw mp::filter::FilterException(
160                         "Bad attribute " + std::string((const char *)
161                                                        attr->name));
162             }
163         }
164         else if (!strcmp((const char *) ptr->name, "db"))
165         {
166             yaz_sparql_t s = yaz_sparql_create();
167             ConfPtr conf(new Conf);
168             conf->s = s;
169             conf->uri = uri;
170
171             const struct _xmlAttr *attr;
172             for (attr = ptr->properties; attr; attr = attr->next)
173             {
174                 if (!strcmp((const char *) attr->name, "path"))
175                     conf->db = mp::xml::get_text(attr->children);
176                 else if (!strcmp((const char *) attr->name, "uri"))
177                     conf->uri = mp::xml::get_text(attr->children);
178                 else if (!strcmp((const char *) attr->name, "schema"))
179                     conf->schema = mp::xml::get_text(attr->children);
180                 else
181                     throw mp::filter::FilterException(
182                         "Bad attribute " + std::string((const char *)
183                                                        attr->name));
184             }
185             xmlNode *p = ptr->children;
186             for (; p; p = p->next)
187             {
188                 if (p->type != XML_ELEMENT_NODE)
189                     continue;
190                 std::string name = (const char *) p->name;
191                 const struct _xmlAttr *attr;
192                 for (attr = p->properties; attr; attr = attr->next)
193                 {
194                     if (!strcmp((const char *) attr->name, "type"))
195                     {
196                         name.append(".");
197                         name.append(mp::xml::get_text(attr->children));
198                     }
199                     else
200                         throw mp::filter::FilterException(
201                             "Bad attribute " + std::string((const char *)
202                                                            attr->name));
203                 }
204                 std::string value = mp::xml::get_text(p);
205                 if (yaz_sparql_add_pattern(s, name.c_str(), value.c_str()))
206                 {
207                     throw mp::filter::FilterException(
208                         "Bad SPARQL config " + name);
209                 }
210             }
211             if (!conf->uri.length())
212             {
213                 throw mp::filter::FilterException("Missing uri");
214             }
215             if (!conf->db.length())
216             {
217                 throw mp::filter::FilterException("Missing path");
218             }
219             db_conf.push_back(conf);
220         }
221         else
222         {
223             throw mp::filter::FilterException
224                 ("Bad element "
225                  + std::string((const char *) ptr->name)
226                  + " in sparql filter");
227         }
228     }
229 }
230
231 yf::SPARQL::Conf::~Conf()
232 {
233     yaz_sparql_destroy(s);
234 }
235
236 yf::SPARQL::Session::Session(const SPARQL *sparql) :
237     m_in_use(true),
238     m_support_named_result_sets(false),
239     m_sparql(sparql)
240 {
241 }
242
243 yf::SPARQL::Session::~Session()
244 {
245 }
246
247 yf::SPARQL::SessionPtr yf::SPARQL::get_session(Package & package,
248                                                Z_APDU **apdu) const
249 {
250     SessionPtr ptr0;
251
252     Z_GDU *gdu = package.request().get();
253
254     boost::mutex::scoped_lock lock(m_p->m_mutex);
255
256     std::map<mp::Session,SPARQL::SessionPtr>::iterator it;
257
258     if (gdu && gdu->which == Z_GDU_Z3950)
259         *apdu = gdu->u.z3950;
260     else
261         *apdu = 0;
262
263     while (true)
264     {
265         it = m_p->m_clients.find(package.session());
266         if (it == m_p->m_clients.end())
267             break;
268         if (!it->second->m_in_use)
269         {
270             it->second->m_in_use = true;
271             return it->second;
272         }
273         m_p->m_cond_session_ready.wait(lock);
274     }
275     if (!*apdu)
276         return ptr0;
277
278     // new Z39.50 session ..
279     SessionPtr p(new Session(this));
280     m_p->m_clients[package.session()] = p;
281     return p;
282 }
283
284 void yf::SPARQL::release_session(Package &package) const
285 {
286     boost::mutex::scoped_lock lock(m_p->m_mutex);
287     std::map<mp::Session,SessionPtr>::iterator it;
288
289     it = m_p->m_clients.find(package.session());
290     if (it != m_p->m_clients.end())
291     {
292         it->second->m_in_use = false;
293
294         if (package.session().is_closed())
295             m_p->m_clients.erase(it);
296         m_p->m_cond_session_ready.notify_all();
297     }
298 }
299
300 static bool get_result(xmlDoc *doc, Odr_int *sz, Odr_int pos, xmlDoc **ndoc)
301 {
302     xmlNode *ptr = xmlDocGetRootElement(doc);
303     xmlNode *q0;
304     Odr_int cur = 0;
305
306     if (ndoc)
307         *ndoc = xmlNewDoc(BAD_CAST "1.0");
308
309     if (ptr->type == XML_ELEMENT_NODE &&
310         !strcmp((const char *) ptr->name, "RDF"))
311     {
312         if (ndoc)
313         {
314             q0 = xmlCopyNode(ptr, 2);
315             xmlDocSetRootElement(*ndoc, q0);
316         }
317         ptr = ptr->children;
318
319         while (ptr && ptr->type != XML_ELEMENT_NODE)
320             ptr = ptr->next;
321         if (ptr && ptr->type == XML_ELEMENT_NODE &&
322             !strcmp((const char *) ptr->name, "Description"))
323         {
324             xmlNode *p = ptr->children;
325
326             while (p && p->type != XML_ELEMENT_NODE)
327                 p = p->next;
328             if (p && p->type == XML_ELEMENT_NODE &&
329                 !strcmp((const char *) p->name, "type"))
330             { /* SELECT RESULT */
331                 for (ptr = ptr->children; ptr; ptr = ptr->next)
332                     if (ptr->type == XML_ELEMENT_NODE &&
333                         !strcmp((const char *) ptr->name, "solution"))
334                     {
335                         if (cur++ == pos)
336                         {
337                             if (ndoc)
338                             {
339                                 xmlNode *q1 = xmlCopyNode(ptr, 1);
340                                 xmlAddChild(q0, q1);
341                             }
342                             break;
343                         }
344                     }
345             }
346             else
347             {   /* CONSTRUCT result */
348                 for (; ptr; ptr = ptr->next)
349                     if (ptr->type == XML_ELEMENT_NODE &&
350                         !strcmp((const char *) ptr->name, "Description"))
351                     {
352                         if (cur++ == pos)
353                         {
354                             if (ndoc)
355                             {
356                                 xmlNode *q1 = xmlCopyNode(ptr, 1);
357                                 xmlAddChild(q0, q1);
358                             }
359                             return true;
360                         }
361                     }
362             }
363         }
364     }
365     else
366     {
367         for (; ptr; ptr = ptr->next)
368             if (ptr->type == XML_ELEMENT_NODE &&
369                 !strcmp((const char *) ptr->name, "sparql"))
370                 break;
371         if (ptr)
372         {
373             if (ndoc)
374             {
375                 q0 = xmlCopyNode(ptr, 2);
376                 xmlDocSetRootElement(*ndoc, q0);
377             }
378             for (ptr = ptr->children; ptr; ptr = ptr->next)
379                 if (ptr->type == XML_ELEMENT_NODE &&
380                     !strcmp((const char *) ptr->name, "results"))
381                     break;
382         }
383         if (ptr)
384         {
385             xmlNode *q1 = 0;
386             if (ndoc)
387             {
388                 q1 = xmlCopyNode(ptr, 0);
389                 xmlAddChild(q0, q1);
390             }
391             for (ptr = ptr->children; ptr; ptr = ptr->next)
392                 if (ptr->type == XML_ELEMENT_NODE &&
393                     !strcmp((const char *) ptr->name, "result"))
394                 {
395                     if (cur++ == pos)
396                     {
397                         if (ndoc)
398                         {
399                             xmlNode *q2 = xmlCopyNode(ptr, 1);
400                             xmlAddChild(q1, q2);
401                         }
402                         return true;
403                     }
404                 }
405         }
406     }
407     if (sz)
408         *sz = cur;
409     return false;
410 }
411
412 Z_Records *yf::SPARQL::Session::fetch(
413     Package &package,
414     FrontendSetPtr fset,
415     ODR odr, Odr_oid *preferredRecordSyntax,
416     Z_ElementSetNames *esn,
417     int start, int number, int &error_code, std::string &addinfo,
418     int *number_returned, int *next_position)
419 {
420     Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
421     std::list<Result>::iterator it = fset->results.begin();
422     const char *schema = 0;
423     bool uri_lookup = false;
424     if (esn && esn->which == Z_ElementSetNames_generic)
425         schema = esn->u.generic;
426
427     for (; it != fset->results.end(); it++)
428     {
429         if (yaz_sparql_lookup_schema(it->conf->s, schema))
430         {
431             uri_lookup = true;
432             break;
433         }
434         if (!schema || !strcmp(esn->u.generic, it->conf->schema.c_str()))
435             break;
436     }
437     if (it == fset->results.end())
438     {
439         rec->which = Z_Records_NSD;
440         rec->u.nonSurrogateDiagnostic =
441             zget_DefaultDiagFormat(
442                 odr,
443                 YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_,
444                 schema);
445         return rec;
446     }
447     rec->which = Z_Records_DBOSD;
448     rec->u.databaseOrSurDiagnostics = (Z_NamePlusRecordList *)
449         odr_malloc(odr, sizeof(Z_NamePlusRecordList));
450     rec->u.databaseOrSurDiagnostics->records = (Z_NamePlusRecord **)
451         odr_malloc(odr, sizeof(Z_NamePlusRecord *) * number);
452     int i;
453     for (i = 0; i < number; i++)
454     {
455         rec->u.databaseOrSurDiagnostics->records[i] = (Z_NamePlusRecord *)
456             odr_malloc(odr, sizeof(Z_NamePlusRecord));
457         Z_NamePlusRecord *npr = rec->u.databaseOrSurDiagnostics->records[i];
458         npr->databaseName = odr_strdup(odr, fset->db.c_str());
459         npr->which = Z_NamePlusRecord_databaseRecord;
460         xmlDoc *ndoc = 0;
461
462         if (!get_result(it->doc, 0, start - 1 + i, &ndoc))
463         {
464             if (ndoc)
465                 xmlFreeDoc(ndoc);
466             break;
467         }
468         xmlNode *ndoc_root = xmlDocGetRootElement(ndoc);
469         if (!ndoc_root)
470         {
471             xmlFreeDoc(ndoc);
472             break;
473         }
474         if (uri_lookup)
475         {
476             std::string uri;
477             xmlNode *n = ndoc_root;
478             while (n)
479             {
480                 if (n->type == XML_ELEMENT_NODE)
481                 {
482                     if (!strcmp((const char *) n->name, "uri"))
483                     {
484                         uri = mp::xml::get_text(n->children);
485
486                     }
487                     n = n->children;
488                 }
489                 else
490                     n = n->next;
491             }
492             if (!uri.length())
493             {
494                 rec->which = Z_Records_NSD;
495                 rec->u.nonSurrogateDiagnostic =
496                     zget_DefaultDiagFormat(
497                         odr,
498                         YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS, 0);
499                 xmlFreeDoc(ndoc);
500                 return rec;
501             }
502             else
503             {
504                 mp::wrbuf addinfo, query, w;
505                 int error = yaz_sparql_from_uri_wrbuf(it->conf->s,
506                                                       addinfo, query,
507                                                       uri.c_str(), schema);
508                 if (!error)
509                 {
510                     yaz_log(YLOG_LOG, "query=%s", query.c_str());
511                     error = invoke_sparql(package, query.c_str(),
512                                           it->conf, w);
513                 }
514                 if (error)
515                 {
516                     rec->which = Z_Records_NSD;
517                     rec->u.nonSurrogateDiagnostic =
518                         zget_DefaultDiagFormat(
519                             odr,
520                             error,
521                             addinfo.len() ? addinfo.c_str() : 0);
522                     xmlFreeDoc(ndoc);
523                     return rec;
524                 }
525                 npr->u.databaseRecord =
526                     z_ext_record_xml(odr, w.c_str(), w.len());
527             }
528         }
529         else
530         {
531             xmlBufferPtr buf = xmlBufferCreate();
532             xmlNodeDump(buf, ndoc, ndoc_root, 0, 0);
533             yaz_log(YLOG_LOG, "record %s %.*s", uri_lookup ? "uri" : "normal",
534                     (int) buf->use, (const char *) buf->content);
535             npr->u.databaseRecord =
536                 z_ext_record_xml(odr, (const char *) buf->content, buf->use);
537             xmlBufferFree(buf);
538         }
539         xmlFreeDoc(ndoc);
540     }
541     rec->u.databaseOrSurDiagnostics->num_records = i;
542     *number_returned = i;
543     if (start + number > fset->hits)
544         *next_position = 0;
545     else
546         *next_position = start + number;
547     return rec;
548 }
549
550 int yf::SPARQL::Session::invoke_sparql(mp::Package &package,
551                                        const char *sparql_query,
552                                        ConfPtr conf,
553                                        WRBUF w)
554 {
555     Package http_package(package.session(), package.origin());
556     mp::odr odr;
557
558     http_package.copy_filter(package);
559     Z_GDU *gdu = z_get_HTTP_Request_uri(odr, conf->uri.c_str(), 0, 1);
560
561     z_HTTP_header_add(odr, &gdu->u.HTTP_Request->headers,
562                       "Content-Type", "application/x-www-form-urlencoded");
563     z_HTTP_header_add(odr, &gdu->u.HTTP_Request->headers,
564                       "Accept", "application/sparql-results+xml,"
565                       "application/rdf+xml");
566     const char *names[2];
567     names[0] = "query";
568     names[1] = 0;
569     const char *values[1];
570     values[0] = sparql_query;
571     char *path = 0;
572     yaz_array_to_uri(&path, odr, (char **) names, (char **) values);
573
574     gdu->u.HTTP_Request->content_buf = path;
575     gdu->u.HTTP_Request->content_len = strlen(path);
576
577     yaz_log(YLOG_LOG, "sparql: HTTP request\n%s", sparql_query);
578
579     http_package.request() = gdu;
580     http_package.move();
581
582     Z_GDU *gdu_resp = http_package.response().get();
583
584     if (!gdu_resp || gdu_resp->which != Z_GDU_HTTP_Response)
585     {
586         wrbuf_puts(w, "no HTTP response from backend");
587         return YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
588     }
589     else if (gdu_resp->u.HTTP_Response->code != 200)
590     {
591         wrbuf_printf(w, "sparql: HTTP error %d from backend",
592                      gdu_resp->u.HTTP_Response->code);
593         return YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
594     }
595     Z_HTTP_Response *resp = gdu_resp->u.HTTP_Response;
596     wrbuf_write(w, resp->content_buf, resp->content_len);
597     return 0;
598 }
599
600 Z_APDU *yf::SPARQL::Session::search(mp::Package &package,
601                                     Z_APDU *apdu_req,
602                                     mp::odr &odr,
603                                     const char *sparql_query,
604                                     ConfPtr conf, FrontendSetPtr fset)
605 {
606     Z_SearchRequest *req = apdu_req->u.searchRequest;
607     Z_APDU *apdu_res = 0;
608     mp::wrbuf w;
609
610     int error = invoke_sparql(package, sparql_query, conf, w);
611     if (error)
612     {
613         apdu_res = odr.create_searchResponse(apdu_req, error,
614                                              w.len() ?
615                                              w.c_str() : 0);
616     }
617     else
618     {
619         xmlDocPtr doc = xmlParseMemory(w.c_str(), w.len());
620         if (!doc)
621         {
622             apdu_res = odr.create_searchResponse(
623                 apdu_req,
624                 YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
625                 "invalid XML from backendbackend");
626         }
627         else
628         {
629             Result result;
630             Z_Records *records = 0;
631             int number_returned = 0;
632             int next_position = 0;
633             int error_code = 0;
634             std::string addinfo;
635
636             result.doc = doc;
637             result.conf = conf;
638             fset->results.push_back(result);
639             yaz_log(YLOG_LOG, "saving sparql result xmldoc=%p", doc);
640
641             get_result(result.doc, &fset->hits, -1, 0);
642             m_frontend_sets[req->resultSetName] = fset;
643
644             result.doc = 0;
645
646             Odr_int number = 0;
647             const char *element_set_name = 0;
648             mp::util::piggyback_sr(req, fset->hits, number, &element_set_name);
649             if (number)
650             {
651                 Z_ElementSetNames *esn;
652
653                 if (number > *req->smallSetUpperBound)
654                     esn = req->mediumSetElementSetNames;
655                 else
656                     esn = req->smallSetElementSetNames;
657                 records = fetch(package, fset,
658                                 odr, req->preferredRecordSyntax, esn,
659                                 1, number,
660                                 error_code, addinfo,
661                                 &number_returned,
662                                 &next_position);
663             }
664             if (error_code)
665             {
666                 apdu_res =
667                     odr.create_searchResponse(
668                         apdu_req, error_code, addinfo.c_str());
669             }
670             else
671             {
672                 apdu_res =
673                     odr.create_searchResponse(apdu_req, 0, 0);
674                 Z_SearchResponse *resp = apdu_res->u.searchResponse;
675                 *resp->resultCount = fset->hits;
676                 *resp->numberOfRecordsReturned = number_returned;
677                 *resp->nextResultSetPosition = next_position;
678                 resp->records = records;
679             }
680         }
681     }
682     return apdu_res;
683 }
684
685 void yf::SPARQL::Session::handle_z(mp::Package &package, Z_APDU *apdu_req)
686 {
687     mp::odr odr;
688     Z_APDU *apdu_res = 0;
689     if (apdu_req->which == Z_APDU_initRequest)
690     {
691         apdu_res = odr.create_initResponse(apdu_req, 0, 0);
692         Z_InitRequest *req = apdu_req->u.initRequest;
693         Z_InitResponse *resp = apdu_res->u.initResponse;
694
695         resp->implementationName = odr_strdup(odr, "sparql");
696         if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
697             m_support_named_result_sets = true;
698         int i;
699         static const int masks[] = {
700             Z_Options_search, Z_Options_present,
701             Z_Options_namedResultSets, -1
702         };
703         for (i = 0; masks[i] != -1; i++)
704             if (ODR_MASK_GET(req->options, masks[i]))
705                 ODR_MASK_SET(resp->options, masks[i]);
706         static const int versions[] = {
707             Z_ProtocolVersion_1,
708             Z_ProtocolVersion_2,
709             Z_ProtocolVersion_3,
710             -1
711         };
712         for (i = 0; versions[i] != -1; i++)
713             if (ODR_MASK_GET(req->protocolVersion, versions[i]))
714                 ODR_MASK_SET(resp->protocolVersion, versions[i]);
715             else
716                 break;
717         *resp->preferredMessageSize = *req->preferredMessageSize;
718         *resp->maximumRecordSize = *req->maximumRecordSize;
719     }
720     else if (apdu_req->which == Z_APDU_close)
721     {
722         apdu_res = odr.create_close(apdu_req,
723                                     Z_Close_finished, 0);
724         package.session().close();
725     }
726     else if (apdu_req->which == Z_APDU_searchRequest)
727     {
728         Z_SearchRequest *req = apdu_req->u.searchRequest;
729
730         FrontendSets::iterator fset_it =
731             m_frontend_sets.find(req->resultSetName);
732         if (fset_it != m_frontend_sets.end())
733         {
734             // result set already exist
735             // if replace indicator is off: we return diagnostic if
736             // result set already exist.
737             if (*req->replaceIndicator == 0)
738             {
739                 Z_APDU *apdu =
740                     odr.create_searchResponse(
741                         apdu_req,
742                         YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
743                         0);
744                 package.response() = apdu;
745             }
746             m_frontend_sets.erase(fset_it);
747         }
748         if (req->query->which != Z_Query_type_1)
749         {
750             apdu_res = odr.create_searchResponse(
751                 apdu_req, YAZ_BIB1_QUERY_TYPE_UNSUPP, 0);
752         }
753         else if (req->num_databaseNames != 1)
754         {
755             apdu_res = odr.create_searchResponse(
756                 apdu_req,
757                 YAZ_BIB1_ACCESS_TO_SPECIFIED_DATABASE_DENIED, 0);
758         }
759         else
760         {
761             std::string db = req->databaseNames[0];
762             std::list<ConfPtr>::const_iterator it;
763             FrontendSetPtr fset(new FrontendSet);
764
765             m_frontend_sets.erase(req->resultSetName);
766             fset->db = db;
767             it = m_sparql->db_conf.begin();
768             for (; it != m_sparql->db_conf.end(); it++)
769                 if (yaz_match_glob((*it)->db.c_str(), db.c_str()))
770                 {
771                     mp::wrbuf addinfo_wr;
772                     mp::wrbuf sparql_wr;
773                     int error =
774                         yaz_sparql_from_rpn_wrbuf((*it)->s,
775                                                   addinfo_wr, sparql_wr,
776                                                   req->query->u.type_1);
777                     if (error)
778                     {
779                         apdu_res = odr.create_searchResponse(
780                             apdu_req, error,
781                             addinfo_wr.len() ? addinfo_wr.c_str() : 0);
782                     }
783                     else
784                     {
785                         Z_APDU *apdu_1 = search(package, apdu_req, odr,
786                                                 sparql_wr.c_str(), *it,
787                                                 fset);
788                         if (!apdu_res)
789                             apdu_res = apdu_1;
790                     }
791                 }
792             if (apdu_res == 0)
793             {
794                 apdu_res = odr.create_searchResponse(
795                     apdu_req, YAZ_BIB1_DATABASE_DOES_NOT_EXIST, db.c_str());
796             }
797         }
798     }
799     else if (apdu_req->which == Z_APDU_presentRequest)
800     {
801         Z_PresentRequest *req = apdu_req->u.presentRequest;
802         FrontendSets::iterator fset_it =
803             m_frontend_sets.find(req->resultSetId);
804         if (fset_it == m_frontend_sets.end())
805         {
806             apdu_res =
807                 odr.create_presentResponse(
808                     apdu_req, YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
809                     req->resultSetId);
810             package.response() = apdu_res;
811             return;
812         }
813         int number_returned = 0;
814         int next_position = 0;
815         int error_code = 0;
816         std::string addinfo;
817         Z_ElementSetNames *esn = 0;
818         if (req->recordComposition)
819         {
820             if (req->recordComposition->which == Z_RecordComp_simple)
821                 esn = req->recordComposition->u.simple;
822             else
823             {
824                 apdu_res =
825                     odr.create_presentResponse(
826                         apdu_req,
827                         YAZ_BIB1_ONLY_A_SINGLE_ELEMENT_SET_NAME_SUPPORTED,
828                         0);
829                 package.response() = apdu_res;
830                 return;
831             }
832         }
833         Z_Records *records = fetch(
834             package,
835             fset_it->second,
836             odr, req->preferredRecordSyntax, esn,
837             *req->resultSetStartPoint, *req->numberOfRecordsRequested,
838             error_code, addinfo,
839             &number_returned,
840             &next_position);
841         if (error_code)
842         {
843             apdu_res =
844                 odr.create_presentResponse(apdu_req, error_code,
845                                            addinfo.c_str());
846         }
847         else
848         {
849             apdu_res =
850                 odr.create_presentResponse(apdu_req, 0, 0);
851             Z_PresentResponse *resp = apdu_res->u.presentResponse;
852             resp->records = records;
853             *resp->numberOfRecordsReturned = number_returned;
854             *resp->nextResultSetPosition = next_position;
855         }
856     }
857     else
858     {
859         apdu_res = odr.create_close(apdu_req,
860                                     Z_Close_protocolError,
861                                     "sparql: unhandled APDU");
862         package.session().close();
863     }
864
865     assert(apdu_res);
866     package.response() = apdu_res;
867 }
868
869 void yf::SPARQL::process(mp::Package &package) const
870 {
871     Z_APDU *apdu;
872     SessionPtr p = get_session(package, &apdu);
873     if (p && apdu)
874     {
875         p->handle_z(package, apdu);
876     }
877     else
878         package.move();
879     release_session(package);
880 }
881
882 static mp::filter::Base* filter_creator()
883 {
884     return new mp::filter::SPARQL;
885 }
886
887 extern "C" {
888     struct metaproxy_1_filter_struct metaproxy_1_filter_sparql = {
889         0,
890         "sparql",
891         filter_creator
892     };
893 }
894
895
896 /*
897  * Local variables:
898  * c-basic-offset: 4
899  * c-file-style: "Stroustrup"
900  * indent-tabs-mode: nil
901  * End:
902  * vim: shiftwidth=4 tabstop=8 expandtab
903  */
904