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