97c8a4da34d40d4130e973174718d14d5ea5544c
[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 *run_sparql(mp::Package &package,
97                                Z_APDU *apdu_req,
98                                mp::odr &odr,
99                                const char *sparql_query,
100                                ConfPtr conf, FrontendSetPtr fset);
101             Z_Records *fetch(
102                 FrontendSetPtr fset,
103                 ODR odr, Odr_oid *preferredRecordSyntax,
104                 Z_ElementSetNames *esn,
105                 int start, int number, int &error_code, std::string &addinfo,
106                 int *number_returned, int *next_position);
107             bool m_in_use;
108         private:
109             bool m_support_named_result_sets;
110             FrontendSets m_frontend_sets;
111             const SPARQL *m_sparql;
112         };
113     }
114 }
115
116 yf::SPARQL::Result::~Result()
117 {
118     if (doc)
119         xmlFreeDoc(doc);
120 }
121
122 yf::SPARQL::Result::Result()
123 {
124     doc = 0;
125 }
126
127 yf::SPARQL::SPARQL() : m_p(new Rep)
128 {
129 }
130
131 yf::SPARQL::~SPARQL()
132 {
133 }
134
135 void yf::SPARQL::configure(const xmlNode *xmlnode, bool test_only,
136                            const char *path)
137 {
138     const xmlNode *ptr = xmlnode->children;
139     std::string uri;
140
141     for (; ptr; ptr = ptr->next)
142     {
143         if (ptr->type != XML_ELEMENT_NODE)
144             continue;
145         if (!strcmp((const char *) ptr->name, "defaults"))
146         {
147             const struct _xmlAttr *attr;
148             for (attr = ptr->properties; attr; attr = attr->next)
149             {
150                 if (!strcmp((const char *) attr->name, "uri"))
151                     uri = mp::xml::get_text(attr->children);
152                 else
153                     throw mp::filter::FilterException(
154                         "Bad attribute " + std::string((const char *)
155                                                        attr->name));
156             }
157         }
158         else if (!strcmp((const char *) ptr->name, "db"))
159         {
160             yaz_sparql_t s = yaz_sparql_create();
161             ConfPtr conf(new Conf);
162             conf->s = s;
163             conf->uri = uri;
164
165             const struct _xmlAttr *attr;
166             for (attr = ptr->properties; attr; attr = attr->next)
167             {
168                 if (!strcmp((const char *) attr->name, "path"))
169                     conf->db = mp::xml::get_text(attr->children);
170                 else if (!strcmp((const char *) attr->name, "uri"))
171                     conf->uri = mp::xml::get_text(attr->children);
172                 else if (!strcmp((const char *) attr->name, "schema"))
173                     conf->schema = mp::xml::get_text(attr->children);
174                 else
175                     throw mp::filter::FilterException(
176                         "Bad attribute " + std::string((const char *)
177                                                        attr->name));
178             }
179             xmlNode *p = ptr->children;
180             for (; p; p = p->next)
181             {
182                 if (p->type != XML_ELEMENT_NODE)
183                     continue;
184                 std::string name = (const char *) p->name;
185                 const struct _xmlAttr *attr;
186                 for (attr = p->properties; attr; attr = attr->next)
187                 {
188                     if (!strcmp((const char *) attr->name, "type"))
189                     {
190                         name.append(".");
191                         name.append(mp::xml::get_text(attr->children));
192                     }
193                     else
194                         throw mp::filter::FilterException(
195                             "Bad attribute " + std::string((const char *)
196                                                            attr->name));
197                 }
198                 std::string value = mp::xml::get_text(p);
199                 if (yaz_sparql_add_pattern(s, name.c_str(), value.c_str()))
200                 {
201                     throw mp::filter::FilterException(
202                         "Bad SPARQL config " + name);
203                 }
204             }
205             if (!conf->uri.length())
206             {
207                 throw mp::filter::FilterException("Missing uri");
208             }
209             if (!conf->db.length())
210             {
211                 throw mp::filter::FilterException("Missing path");
212             }
213             db_conf.push_back(conf);
214         }
215         else
216         {
217             throw mp::filter::FilterException
218                 ("Bad element "
219                  + std::string((const char *) ptr->name)
220                  + " in sparql filter");
221         }
222     }
223 }
224
225 yf::SPARQL::Conf::~Conf()
226 {
227     yaz_sparql_destroy(s);
228 }
229
230 yf::SPARQL::Session::Session(const SPARQL *sparql) :
231     m_in_use(true),
232     m_support_named_result_sets(false),
233     m_sparql(sparql)
234 {
235 }
236
237 yf::SPARQL::Session::~Session()
238 {
239 }
240
241 yf::SPARQL::SessionPtr yf::SPARQL::get_session(Package & package,
242                                                Z_APDU **apdu) const
243 {
244     SessionPtr ptr0;
245
246     Z_GDU *gdu = package.request().get();
247
248     boost::mutex::scoped_lock lock(m_p->m_mutex);
249
250     std::map<mp::Session,SPARQL::SessionPtr>::iterator it;
251
252     if (gdu && gdu->which == Z_GDU_Z3950)
253         *apdu = gdu->u.z3950;
254     else
255         *apdu = 0;
256
257     while (true)
258     {
259         it = m_p->m_clients.find(package.session());
260         if (it == m_p->m_clients.end())
261             break;
262         if (!it->second->m_in_use)
263         {
264             it->second->m_in_use = true;
265             return it->second;
266         }
267         m_p->m_cond_session_ready.wait(lock);
268     }
269     if (!*apdu)
270         return ptr0;
271
272     // new Z39.50 session ..
273     SessionPtr p(new Session(this));
274     m_p->m_clients[package.session()] = p;
275     return p;
276 }
277
278 void yf::SPARQL::release_session(Package &package) const
279 {
280     boost::mutex::scoped_lock lock(m_p->m_mutex);
281     std::map<mp::Session,SessionPtr>::iterator it;
282
283     it = m_p->m_clients.find(package.session());
284     if (it != m_p->m_clients.end())
285     {
286         it->second->m_in_use = false;
287
288         if (package.session().is_closed())
289             m_p->m_clients.erase(it);
290         m_p->m_cond_session_ready.notify_all();
291     }
292 }
293
294 static bool get_result(xmlDoc *doc, Odr_int *sz, Odr_int pos,
295                        xmlDoc **ndoc)
296 {
297     xmlNode *ptr = xmlDocGetRootElement(doc);
298     xmlNode *q0;
299     Odr_int cur = 0;
300
301     if (ndoc)
302         *ndoc = xmlNewDoc(BAD_CAST "1.0");
303
304     if (ptr->type == XML_ELEMENT_NODE &&
305         !strcmp((const char *) ptr->name, "RDF"))
306     {
307         if (ndoc)
308         {
309             q0 = xmlCopyNode(ptr, 2);
310             xmlDocSetRootElement(*ndoc, q0);
311         }
312         ptr = ptr->children;
313
314         while (ptr && ptr->type != XML_ELEMENT_NODE)
315             ptr = ptr->next;
316         if (ptr && ptr->type == XML_ELEMENT_NODE &&
317             !strcmp((const char *) ptr->name, "Description"))
318         {
319             xmlNode *p = ptr->children;
320
321             while (p && p->type != XML_ELEMENT_NODE)
322                 p = p->next;
323             if (p && p->type == XML_ELEMENT_NODE &&
324                 !strcmp((const char *) p->name, "type"))
325             { /* SELECT RESULT */
326                 for (ptr = ptr->children; ptr; ptr = ptr->next)
327                     if (ptr->type == XML_ELEMENT_NODE &&
328                         !strcmp((const char *) ptr->name, "solution"))
329                     {
330                         if (cur++ == pos)
331                         {
332                             if (ndoc)
333                             {
334                                 xmlNode *q1 = xmlCopyNode(ptr, 1);
335                                 xmlAddChild(q0, q1);
336                             }
337                             break;
338                         }
339                     }
340             }
341             else
342             {   /* CONSTRUCT result */
343                 for (; ptr; ptr = ptr->next)
344                     if (ptr->type == XML_ELEMENT_NODE &&
345                         !strcmp((const char *) ptr->name, "Description"))
346                     {
347                         if (cur++ == pos)
348                         {
349                             if (ndoc)
350                             {
351                                 xmlNode *q1 = xmlCopyNode(ptr, 1);
352                                 xmlAddChild(q0, q1);
353                             }
354                             return true;
355                         }
356                     }
357             }
358         }
359     }
360     else
361     {
362         for (; ptr; ptr = ptr->next)
363             if (ptr->type == XML_ELEMENT_NODE &&
364                 !strcmp((const char *) ptr->name, "sparql"))
365                 break;
366         if (ptr)
367         {
368             if (ndoc)
369             {
370                 q0 = xmlCopyNode(ptr, 2);
371                 xmlDocSetRootElement(*ndoc, q0);
372             }
373             for (ptr = ptr->children; ptr; ptr = ptr->next)
374                 if (ptr->type == XML_ELEMENT_NODE &&
375                     !strcmp((const char *) ptr->name, "results"))
376                     break;
377         }
378         if (ptr)
379         {
380             xmlNode *q1 = 0;
381             if (ndoc)
382             {
383                 q1 = xmlCopyNode(ptr, 0);
384                 xmlAddChild(q0, q1);
385             }
386             for (ptr = ptr->children; ptr; ptr = ptr->next)
387                 if (ptr->type == XML_ELEMENT_NODE &&
388                     !strcmp((const char *) ptr->name, "result"))
389                 {
390                     if (cur++ == pos)
391                     {
392                         if (ndoc)
393                         {
394                             xmlNode *q2 = xmlCopyNode(ptr, 1);
395                             xmlAddChild(q1, q2);
396                         }
397                         return true;
398                     }
399                 }
400         }
401     }
402     if (sz)
403         *sz = cur;
404     return false;
405 }
406
407 Z_Records *yf::SPARQL::Session::fetch(
408     FrontendSetPtr fset,
409     ODR odr, Odr_oid *preferredRecordSyntax,
410     Z_ElementSetNames *esn,
411     int start, int number, int &error_code, std::string &addinfo,
412     int *number_returned, int *next_position)
413 {
414     Z_Records *rec = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
415     std::list<Result>::iterator it = fset->results.begin();
416     if (esn && esn->which == Z_ElementSetNames_generic && esn->u.generic)
417     {
418         for (; it != fset->results.end(); it++)
419         {
420             yaz_log(YLOG_LOG, "checking xmldoc=%p schema=%s user-schema=%s",
421                     it->doc, it->conf->schema.c_str(), esn->u.generic);
422             if (!strcmp(esn->u.generic, it->conf->schema.c_str()))
423                 break;
424         }
425         if (it == fset->results.end())
426         {
427             rec->which = Z_Records_NSD;
428             rec->u.nonSurrogateDiagnostic =
429                 zget_DefaultDiagFormat(
430                     odr,
431                     YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_,
432                     esn->u.generic);
433             return rec;
434         }
435     }
436     rec->which = Z_Records_DBOSD;
437     rec->u.databaseOrSurDiagnostics = (Z_NamePlusRecordList *)
438         odr_malloc(odr, sizeof(Z_NamePlusRecordList));
439     rec->u.databaseOrSurDiagnostics->records = (Z_NamePlusRecord **)
440         odr_malloc(odr, sizeof(Z_NamePlusRecord *) * number);
441     int i;
442     for (i = 0; i < number; i++)
443     {
444         rec->u.databaseOrSurDiagnostics->records[i] = (Z_NamePlusRecord *)
445             odr_malloc(odr, sizeof(Z_NamePlusRecord));
446         Z_NamePlusRecord *npr = rec->u.databaseOrSurDiagnostics->records[i];
447         npr->databaseName = odr_strdup(odr, fset->db.c_str());
448         npr->which = Z_NamePlusRecord_databaseRecord;
449         xmlDoc *ndoc = 0;
450
451         if (!get_result(it->doc, 0, start - 1 + i, &ndoc))
452         {
453             if (ndoc)
454                 xmlFreeDoc(ndoc);
455             break;
456         }
457         xmlNode *ndoc_root = xmlDocGetRootElement(ndoc);
458         if (!ndoc_root)
459         {
460             xmlFreeDoc(ndoc);
461             break;
462         }
463         xmlBufferPtr buf = xmlBufferCreate();
464         xmlNodeDump(buf, ndoc, ndoc_root, 0, 0);
465         npr->u.databaseRecord =
466             z_ext_record_xml(odr, (const char *) buf->content, buf->use);
467         xmlFreeDoc(ndoc);
468         xmlBufferFree(buf);
469     }
470     rec->u.databaseOrSurDiagnostics->num_records = i;
471     *number_returned = i;
472     if (start + number > fset->hits)
473         *next_position = 0;
474     else
475         *next_position = start + number;
476     return rec;
477 }
478
479 Z_APDU *yf::SPARQL::Session::run_sparql(mp::Package &package,
480                                         Z_APDU *apdu_req,
481                                         mp::odr &odr,
482                                         const char *sparql_query,
483                                         ConfPtr conf, FrontendSetPtr fset)
484 {
485     Z_SearchRequest *req = apdu_req->u.searchRequest;
486     Package http_package(package.session(), package.origin());
487
488     http_package.copy_filter(package);
489     Z_GDU *gdu = z_get_HTTP_Request_uri(odr, conf->uri.c_str(), 0, 1);
490
491     z_HTTP_header_add(odr, &gdu->u.HTTP_Request->headers,
492                       "Content-Type", "application/x-www-form-urlencoded");
493     z_HTTP_header_add(odr, &gdu->u.HTTP_Request->headers,
494                       "Accept", "application/sparql-results+xml,"
495                       "application/rdf+xml");
496     const char *names[2];
497     names[0] = "query";
498     names[1] = 0;
499     const char *values[1];
500     values[0] = sparql_query;
501     char *path = 0;
502     yaz_array_to_uri(&path, odr, (char **) names, (char **) values);
503
504     gdu->u.HTTP_Request->content_buf = path;
505     gdu->u.HTTP_Request->content_len = strlen(path);
506
507     yaz_log(YLOG_LOG, "sparql: HTTP request\n%s", sparql_query);
508
509     http_package.request() = gdu;
510     http_package.move();
511
512     Z_GDU *gdu_resp = http_package.response().get();
513     Z_APDU *apdu_res = 0;
514     if (!gdu_resp || gdu_resp->which != Z_GDU_HTTP_Response)
515     {
516         yaz_log(YLOG_LOG, "sparql: no HTTP response");
517         apdu_res = odr.create_searchResponse(apdu_req,
518                                              YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
519                                              "no HTTP response from backend");
520     }
521     else if (gdu_resp->u.HTTP_Response->code != 200)
522     {
523         mp::wrbuf w;
524
525         wrbuf_printf(w, "sparql: HTTP error %d from backend",
526                      gdu_resp->u.HTTP_Response->code);
527         apdu_res = odr.create_searchResponse(apdu_req,
528                                              YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
529                                              w.c_str());
530     }
531     else
532     {
533         Z_HTTP_Response *resp = gdu_resp->u.HTTP_Response;
534         xmlDocPtr doc = xmlParseMemory(resp->content_buf, resp->content_len);
535         if (!doc)
536             apdu_res = odr.create_searchResponse(apdu_req,
537                                              YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
538                                              "invalid XML from backendbackend");
539         else
540         {
541             Result result;
542             Z_Records *records = 0;
543             int number_returned = 0;
544             int next_position = 0;
545             int error_code = 0;
546             std::string addinfo;
547
548             result.doc = doc;
549             result.conf = conf;
550             fset->results.push_back(result);
551             yaz_log(YLOG_LOG, "saving sparql result xmldoc=%p", doc);
552
553             get_result(result.doc, &fset->hits, -1, 0);
554             m_frontend_sets[req->resultSetName] = fset;
555
556             result.doc = 0;
557
558             Odr_int number = 0;
559             const char *element_set_name = 0;
560             mp::util::piggyback_sr(req, fset->hits, number, &element_set_name);
561             if (number)
562             {
563                 Z_ElementSetNames *esn;
564
565                 if (number > *req->smallSetUpperBound)
566                     esn = req->mediumSetElementSetNames;
567                 else
568                     esn = req->smallSetElementSetNames;
569                 records = fetch(fset,
570                                 odr, req->preferredRecordSyntax, esn,
571                                 1, number,
572                                 error_code, addinfo,
573                                 &number_returned,
574                                 &next_position);
575             }
576             if (error_code)
577             {
578                 apdu_res =
579                     odr.create_searchResponse(
580                         apdu_req, error_code, addinfo.c_str());
581             }
582             else
583             {
584                 apdu_res =
585                     odr.create_searchResponse(apdu_req, 0, 0);
586                 Z_SearchResponse *resp = apdu_res->u.searchResponse;
587                 *resp->resultCount = fset->hits;
588                 *resp->numberOfRecordsReturned = number_returned;
589                 *resp->nextResultSetPosition = next_position;
590                 resp->records = records;
591             }
592         }
593     }
594     return apdu_res;
595 }
596
597 void yf::SPARQL::Session::handle_z(mp::Package &package, Z_APDU *apdu_req)
598 {
599     mp::odr odr;
600     Z_APDU *apdu_res = 0;
601     if (apdu_req->which == Z_APDU_initRequest)
602     {
603         apdu_res = odr.create_initResponse(apdu_req, 0, 0);
604         Z_InitRequest *req = apdu_req->u.initRequest;
605         Z_InitResponse *resp = apdu_res->u.initResponse;
606
607         resp->implementationName = odr_strdup(odr, "sparql");
608         if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
609             m_support_named_result_sets = true;
610         int i;
611         static const int masks[] = {
612             Z_Options_search, Z_Options_present,
613             Z_Options_namedResultSets, -1
614         };
615         for (i = 0; masks[i] != -1; i++)
616             if (ODR_MASK_GET(req->options, masks[i]))
617                 ODR_MASK_SET(resp->options, masks[i]);
618         static const int versions[] = {
619             Z_ProtocolVersion_1,
620             Z_ProtocolVersion_2,
621             Z_ProtocolVersion_3,
622             -1
623         };
624         for (i = 0; versions[i] != -1; i++)
625             if (ODR_MASK_GET(req->protocolVersion, versions[i]))
626                 ODR_MASK_SET(resp->protocolVersion, versions[i]);
627             else
628                 break;
629         *resp->preferredMessageSize = *req->preferredMessageSize;
630         *resp->maximumRecordSize = *req->maximumRecordSize;
631     }
632     else if (apdu_req->which == Z_APDU_close)
633     {
634         apdu_res = odr.create_close(apdu_req,
635                                     Z_Close_finished, 0);
636         package.session().close();
637     }
638     else if (apdu_req->which == Z_APDU_searchRequest)
639     {
640         Z_SearchRequest *req = apdu_req->u.searchRequest;
641
642         FrontendSets::iterator fset_it =
643             m_frontend_sets.find(req->resultSetName);
644         if (fset_it != m_frontend_sets.end())
645         {
646             // result set already exist
647             // if replace indicator is off: we return diagnostic if
648             // result set already exist.
649             if (*req->replaceIndicator == 0)
650             {
651                 Z_APDU *apdu =
652                     odr.create_searchResponse(
653                         apdu_req,
654                         YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
655                         0);
656                 package.response() = apdu;
657             }
658             m_frontend_sets.erase(fset_it);
659         }
660         if (req->query->which != Z_Query_type_1)
661         {
662             apdu_res = odr.create_searchResponse(
663                 apdu_req, YAZ_BIB1_QUERY_TYPE_UNSUPP, 0);
664         }
665         else if (req->num_databaseNames != 1)
666         {
667             apdu_res = odr.create_searchResponse(
668                 apdu_req,
669                 YAZ_BIB1_ACCESS_TO_SPECIFIED_DATABASE_DENIED, 0);
670         }
671         else
672         {
673             std::string db = req->databaseNames[0];
674             std::list<ConfPtr>::const_iterator it;
675             FrontendSetPtr fset(new FrontendSet);
676
677             m_frontend_sets.erase(req->resultSetName);
678             fset->db = db;
679             it = m_sparql->db_conf.begin();
680             for (; it != m_sparql->db_conf.end(); it++)
681                 if (yaz_match_glob((*it)->db.c_str(), db.c_str()))
682                 {
683                     WRBUF addinfo_wr = wrbuf_alloc();
684                     WRBUF sparql_wr = wrbuf_alloc();
685                     int error =
686                         yaz_sparql_from_rpn_wrbuf((*it)->s,
687                                                   addinfo_wr, sparql_wr,
688                                                   req->query->u.type_1);
689                     if (error)
690                     {
691                         apdu_res = odr.create_searchResponse(
692                             apdu_req, error,
693                             wrbuf_len(addinfo_wr) ?
694                             wrbuf_cstr(addinfo_wr) : 0);
695                     }
696                     else
697                     {
698                         Z_APDU *apdu_1 = run_sparql(package, apdu_req, odr,
699                                                     wrbuf_cstr(sparql_wr), *it,
700                                                     fset);
701                         if (!apdu_res)
702                             apdu_res = apdu_1;
703                     }
704                     wrbuf_destroy(addinfo_wr);
705                     wrbuf_destroy(sparql_wr);
706                 }
707             if (apdu_res == 0)
708             {
709                 apdu_res = odr.create_searchResponse(
710                     apdu_req, YAZ_BIB1_DATABASE_DOES_NOT_EXIST, db.c_str());
711             }
712         }
713     }
714     else if (apdu_req->which == Z_APDU_presentRequest)
715     {
716         Z_PresentRequest *req = apdu_req->u.presentRequest;
717         FrontendSets::iterator fset_it =
718             m_frontend_sets.find(req->resultSetId);
719         if (fset_it == m_frontend_sets.end())
720         {
721             apdu_res =
722                 odr.create_presentResponse(
723                     apdu_req, YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
724                     req->resultSetId);
725             package.response() = apdu_res;
726             return;
727         }
728         int number_returned = 0;
729         int next_position = 0;
730         int error_code = 0;
731         std::string addinfo;
732         Z_ElementSetNames *esn = 0;
733         if (req->recordComposition)
734         {
735             if (req->recordComposition->which == Z_RecordComp_simple)
736                 esn = req->recordComposition->u.simple;
737             else
738             {
739                 apdu_res =
740                     odr.create_presentResponse(
741                         apdu_req,
742                         YAZ_BIB1_ONLY_A_SINGLE_ELEMENT_SET_NAME_SUPPORTED,
743                         0);
744                 package.response() = apdu_res;
745                 return;
746             }
747         }
748         Z_Records *records = fetch(
749             fset_it->second,
750             odr, req->preferredRecordSyntax, esn,
751             *req->resultSetStartPoint, *req->numberOfRecordsRequested,
752             error_code, addinfo,
753             &number_returned,
754             &next_position);
755         if (error_code)
756         {
757             apdu_res =
758                 odr.create_presentResponse(apdu_req, error_code,
759                                            addinfo.c_str());
760         }
761         else
762         {
763             apdu_res =
764                 odr.create_presentResponse(apdu_req, 0, 0);
765             Z_PresentResponse *resp = apdu_res->u.presentResponse;
766             resp->records = records;
767             *resp->numberOfRecordsReturned = number_returned;
768             *resp->nextResultSetPosition = next_position;
769         }
770     }
771     else
772     {
773         apdu_res = odr.create_close(apdu_req,
774                                     Z_Close_protocolError,
775                                     "sparql: unhandled APDU");
776         package.session().close();
777     }
778
779     assert(apdu_res);
780     package.response() = apdu_res;
781 }
782
783 void yf::SPARQL::process(mp::Package &package) const
784 {
785     Z_APDU *apdu;
786     SessionPtr p = get_session(package, &apdu);
787     if (p && apdu)
788     {
789         p->handle_z(package, apdu);
790     }
791     else
792         package.move();
793     release_session(package);
794 }
795
796 static mp::filter::Base* filter_creator()
797 {
798     return new mp::filter::SPARQL;
799 }
800
801 extern "C" {
802     struct metaproxy_1_filter_struct metaproxy_1_filter_sparql = {
803         0,
804         "sparql",
805         filter_creator
806     };
807 }
808
809
810 /*
811  * Local variables:
812  * c-basic-offset: 4
813  * c-file-style: "Stroustrup"
814  * indent-tabs-mode: nil
815  * End:
816  * vim: shiftwidth=4 tabstop=8 expandtab
817  */
818