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