17c4ac574336b364076cc74dc9af6405fb59b585
[yaz-moved-to-github.git] / src / zoom-record-cache.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file zoom-record-cache.c
7  * \brief Implements ZOOM record caching
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <assert.h>
14 #include <string.h>
15 #include <errno.h>
16 #include "zoom-p.h"
17
18 #include <yaz/diagbib1.h>
19 #include <yaz/record_render.h>
20 #include <yaz/shptr.h>
21
22 #if SHPTR
23 YAZ_SHPTR_TYPE(WRBUF)
24 #endif
25
26 struct ZOOM_record_p {
27     ODR odr;
28 #if SHPTR
29     struct WRBUF_shptr *record_wrbuf;
30 #else
31     WRBUF wrbuf;
32 #endif
33
34     Z_NamePlusRecord *npr;
35     const char *schema;
36
37     const char *diag_uri;
38     const char *diag_message;
39     const char *diag_details;
40     const char *diag_set;
41 };
42
43 struct ZOOM_record_cache_p {
44     struct ZOOM_record_p rec;
45     char *elementSetName;
46     char *syntax;
47     char *schema;
48     int pos;
49     ZOOM_record_cache next;
50 };
51
52 static size_t record_hash(int pos)
53 {
54     if (pos < 0)
55         pos = 0;
56     return pos % RECORD_HASH_SIZE;
57 }
58
59 static ZOOM_record record_cache_add(ZOOM_resultset r,
60                                     Z_NamePlusRecord *npr,
61                                     int pos,
62                                     const char *syntax,
63                                     const char *elementSetName,
64                                     const char *schema,
65                                     Z_SRW_diagnostic *diag)
66 {
67     ZOOM_record_cache rc = 0;
68
69     ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
70     ZOOM_connection_put_event(r->connection, event);
71
72     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
73     {
74         if (pos == rc->pos
75             && yaz_strcmp_null(schema, rc->schema) == 0
76             && yaz_strcmp_null(elementSetName,rc->elementSetName) == 0
77             && yaz_strcmp_null(syntax, rc->syntax) == 0)
78             break;
79     }
80     if (!rc)
81     {
82         rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
83         rc->rec.odr = 0;
84 #if SHPTR
85         YAZ_SHPTR_INC(r->record_wrbuf);
86         rc->rec.record_wrbuf = r->record_wrbuf;
87 #else
88         rc->rec.wrbuf = 0;
89 #endif
90         rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
91
92         rc->syntax = odr_strdup_null(r->odr, syntax);
93
94         rc->schema = odr_strdup_null(r->odr, schema);
95
96         rc->pos = pos;
97         rc->next = r->record_hash[record_hash(pos)];
98         r->record_hash[record_hash(pos)] = rc;
99
100     }
101
102     rc->rec.npr = npr;
103     rc->rec.schema = odr_strdup_null(r->odr, schema);
104     rc->rec.diag_set = 0;
105     rc->rec.diag_uri = 0;
106     rc->rec.diag_message = 0;
107     rc->rec.diag_details = 0;
108     if (diag)
109     {
110         if (diag->uri)
111         {
112             char *cp;
113             rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
114             if ((cp = strrchr(rc->rec.diag_set, '/')))
115                 *cp = '\0';
116             rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
117         }
118         rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);
119         rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
120     }
121     return &rc->rec;
122 }
123
124 void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr,
125                            int pos,
126                            const char *syntax, const char *elementSetName,
127                            const char *schema,
128                            Z_SRW_diagnostic *diag)
129 {
130     record_cache_add(r, npr, pos, syntax, elementSetName, schema, diag);
131 #if HAVE_LIBMEMCACHED_MEMCACHED_H
132     if (r->connection->mc_st &&
133         !diag && npr->which == Z_NamePlusRecord_databaseRecord &&
134         npr->u.databaseRecord->which == Z_External_octet)
135     {
136         WRBUF k = wrbuf_alloc();
137         uint32_t flags = 0;
138         memcached_return_t rc;
139         time_t expiration = 36000;
140
141         wrbuf_write(k, wrbuf_buf(r->mc_key), wrbuf_len(r->mc_key));
142         wrbuf_printf(k, ";%d;%s;%s;%s", pos,
143                      syntax ? syntax : "",
144                      elementSetName ? elementSetName : "",
145                      schema ? schema : "");
146         rc = memcached_set(r->connection->mc_st,
147                            wrbuf_buf(k),wrbuf_len(k),
148                            npr->u.databaseRecord->u.octet_aligned->buf,
149                            npr->u.databaseRecord->u.octet_aligned->len,
150                            expiration, flags);
151
152         yaz_log(YLOG_LOG, "Store record key=%s rc=%u %s",
153                 wrbuf_cstr(k), (unsigned) rc,
154                 memcached_last_error_message(r->connection->mc_st));
155         wrbuf_destroy(k);
156     }
157 #endif
158 }
159
160
161 ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
162                                      const char *syntax,
163                                      const char *elementSetName,
164                                      const char *schema)
165 {
166     ZOOM_record_cache rc;
167
168     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
169     {
170         if (pos == rc->pos)
171         {
172             if (yaz_strcmp_null(schema, rc->schema))
173                 continue;
174             if (yaz_strcmp_null(elementSetName,rc->elementSetName))
175                 continue;
176             if (yaz_strcmp_null(syntax, rc->syntax))
177                 continue;
178             return &rc->rec;
179         }
180     }
181 #if HAVE_LIBMEMCACHED_MEMCACHED_H
182     if (r->connection && r->connection->mc_st)
183     {
184         WRBUF k = wrbuf_alloc();
185         size_t v_len;
186         char *v;
187         uint32_t flags;
188         memcached_return_t rc;
189
190         wrbuf_write(k, wrbuf_buf(r->mc_key), wrbuf_len(r->mc_key));
191         wrbuf_printf(k, ";%d;%s;%s;%s", pos,
192                      syntax ? syntax : "",
193                      elementSetName ? elementSetName : "",
194                      schema ? schema : "");
195
196         v = memcached_get(r->connection->mc_st, wrbuf_buf(k), wrbuf_len(k),
197                           &v_len, &flags, &rc);
198         wrbuf_destroy(k);
199         if (v)
200         {
201             yaz_log(YLOG_LOG, "Building record from memcached!! syntax=%s",
202                 syntax);
203             Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
204                 odr_malloc(r->odr, sizeof(Z_NamePlusRecord));
205             npr->databaseName = 0;
206             npr->which = Z_NamePlusRecord_databaseRecord;
207             npr->u.databaseRecord = (Z_External *)
208                 odr_malloc(r->odr, sizeof(Z_External));
209             npr->u.databaseRecord->descriptor = 0;
210             npr->u.databaseRecord->direct_reference =
211                 syntax ?
212                 yaz_string_to_oid_odr(yaz_oid_std(), CLASS_RECSYN,
213                                       syntax, r->odr) : 0;
214             npr->u.databaseRecord->indirect_reference = 0;
215             npr->u.databaseRecord->which = Z_External_octet;
216             npr->u.databaseRecord->u.octet_aligned =
217                 odr_create_Odr_oct(r->odr, v, v_len);
218             free(v);
219
220             if (v)
221                 return record_cache_add(r, npr, pos, syntax, elementSetName,
222                                         schema, 0);
223         }
224     }
225 #endif
226     return 0;
227 }
228
229 ZOOM_API(ZOOM_record)
230     ZOOM_record_clone(ZOOM_record srec)
231 {
232     char *buf;
233     int size;
234     ODR odr_enc;
235     ZOOM_record nrec;
236
237     odr_enc = odr_createmem(ODR_ENCODE);
238     if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
239         return 0;
240     buf = odr_getbuf(odr_enc, &size, 0);
241
242     nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
243     nrec->odr = odr_createmem(ODR_DECODE);
244 #if SHPTR
245     nrec->record_wrbuf = 0;
246 #else
247     nrec->wrbuf = 0;
248 #endif
249     odr_setbuf(nrec->odr, buf, size, 0);
250     z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
251
252     nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
253     nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
254     nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
255     nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
256     nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
257     odr_destroy(odr_enc);
258     return nrec;
259 }
260
261 static void ZOOM_record_release(ZOOM_record rec)
262 {
263     if (!rec)
264         return;
265
266 #if SHPTR
267     if (rec->record_wrbuf)
268         YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
269 #else
270     if (rec->wrbuf)
271         wrbuf_destroy(rec->wrbuf);
272 #endif
273
274     if (rec->odr)
275         odr_destroy(rec->odr);
276 }
277
278 ZOOM_API(void)
279     ZOOM_resultset_cache_reset(ZOOM_resultset r)
280 {
281     int i;
282     for (i = 0; i<RECORD_HASH_SIZE; i++)
283     {
284         ZOOM_record_cache rc;
285         for (rc = r->record_hash[i]; rc; rc = rc->next)
286         {
287             ZOOM_record_release(&rc->rec);
288         }
289         r->record_hash[i] = 0;
290     }
291 }
292
293
294 ZOOM_API(const char *)
295     ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
296 {
297     WRBUF wrbuf;
298
299     if (len)
300         *len = 0; /* default return */
301
302     if (!rec || !rec->npr)
303         return 0;
304
305 #if SHPTR
306     if (!rec->record_wrbuf)
307     {
308         WRBUF w = wrbuf_alloc();
309         YAZ_SHPTR_INIT(rec->record_wrbuf, w);
310     }
311     wrbuf = rec->record_wrbuf->ptr;
312 #else
313     if (!rec->wrbuf)
314         rec->wrbuf = wrbuf_alloc();
315     wrbuf = rec->wrbuf;
316 #endif
317     return yaz_record_render(rec->npr, rec->schema, wrbuf, type_spec, len);
318 }
319
320 ZOOM_API(int)
321     ZOOM_record_error(ZOOM_record rec, const char **cp,
322                       const char **addinfo, const char **diagset)
323 {
324     Z_NamePlusRecord *npr;
325
326     if (!rec)
327         return 0;
328
329     npr = rec->npr;
330     if (rec->diag_uri)
331     {
332         if (cp)
333             *cp = rec->diag_message;
334         if (addinfo)
335             *addinfo = rec->diag_details;
336         if (diagset)
337             *diagset = rec->diag_set;
338         return ZOOM_uri_to_code(rec->diag_uri);
339     }
340     if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
341     {
342         Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
343         int error = YAZ_BIB1_UNSPECIFIED_ERROR;
344         const char *add = 0;
345
346         if (diag_rec->which == Z_DiagRec_defaultFormat)
347         {
348             Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
349             oid_class oclass;
350
351             error = *ddf->condition;
352             switch (ddf->which)
353             {
354             case Z_DefaultDiagFormat_v2Addinfo:
355                 add = ddf->u.v2Addinfo;
356                 break;
357             case Z_DefaultDiagFormat_v3Addinfo:
358                 add = ddf->u.v3Addinfo;
359                 break;
360             }
361             if (diagset)
362                 *diagset =
363                     yaz_oid_to_string(yaz_oid_std(),
364                                       ddf->diagnosticSetId, &oclass);
365         }
366         else
367         {
368             if (diagset)
369                 *diagset = "Bib-1";
370         }
371         if (addinfo)
372             *addinfo = add ? add : "";
373         if (cp)
374             *cp = diagbib1_str(error);
375         return error;
376     }
377     return 0;
378 }
379
380 ZOOM_API(void)
381     ZOOM_record_destroy(ZOOM_record rec)
382 {
383     ZOOM_record_release(rec);
384     xfree(rec);
385 }
386
387 /*
388  * Local variables:
389  * c-basic-offset: 4
390  * c-file-style: "Stroustrup"
391  * indent-tabs-mode: nil
392  * End:
393  * vim: shiftwidth=4 tabstop=8 expandtab
394  */
395