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