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