Deal with two common places for corrupt Explain database
[idzebra-moved-to-github.git] / index / zinfo.c
1 /* $Id: zinfo.c,v 1.77 2007-02-24 16:47:16 adam Exp $
2    Copyright (C) 1995-2007
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <sys/types.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include <idzebra/version.h>
30 #include "zinfo.h"
31
32 #define ZINFO_DEBUG 0
33
34 struct zebSUInfo {
35     int index_type;
36     zinfo_index_category_t cat;
37 #define ZEB_SU_SET_USE 1
38 #define ZEB_SU_STR 2
39     int which;
40     union {
41         char *str;
42     } u;
43     int ordinal;
44     zint doc_occurrences;
45     zint term_occurrences;
46 };
47
48 struct zebSUInfoB {
49     struct zebSUInfo info;
50     struct zebSUInfoB *next;
51 };
52
53 typedef struct zebAccessObjectB *zebAccessObject;
54 struct zebAccessObjectB {
55     void *handle;
56     zint sysno;
57     Odr_oid *oid;
58     zebAccessObject next;
59 };
60
61 typedef struct zebAccessInfoB *zebAccessInfo;
62 struct zebAccessInfoB {
63     zebAccessObject attributeSetIds;
64     zebAccessObject schemas;
65 };
66
67 typedef struct {
68     struct zebSUInfoB *SUInfo;
69     zint sysno;
70     int dirty;
71     int readFlag;
72     data1_node *data1_tree;
73 } *zebAttributeDetails;
74
75 struct zebDatabaseInfoB {
76     zebAttributeDetails attributeDetails;
77     int ordinalDatabase;
78     char *databaseName;
79     data1_node *data1_database;
80     zint recordCount;    /* records in db */
81     zint recordBytes;    /* size of records */
82     zint sysno;          /* sysno of database info */
83     int readFlag;        /* 1: read is needed when referenced; 0 if not */
84     int dirty;           /* 1: database is dirty: write is needed */
85     struct zebDatabaseInfoB *next;
86     zebAccessInfo accessInfo;
87 };
88
89 struct zebraExplainAttset {
90     char *name;
91     int ordinal;
92     struct zebraExplainAttset *next;
93 };
94
95 struct zebraCategoryListInfo {
96     int dirty;
97     zint sysno;
98     data1_node *data1_categoryList;
99 };
100
101 struct zebraExplainInfo {
102     int ordinalSU;
103     int ordinalDatabase;
104     zint runNumber;
105     int dirty;
106     int write_flag;
107     Records records;
108     data1_handle dh;
109     Res res;
110     struct zebraExplainAttset *attsets;
111     NMEM nmem;
112     data1_node *data1_target;
113     struct zebraCategoryListInfo *categoryList;
114     struct zebDatabaseInfoB *databaseInfo;
115     struct zebDatabaseInfoB *curDatabaseInfo;
116     zebAccessInfo accessInfo;
117     char date[15]; /* YYYY MMDD HH MM SS */
118     ZebraExplainUpdateFunc *updateFunc;
119     void *updateHandle;
120 };
121
122 static void zebraExplain_initCommonInfo(ZebraExplainInfo zei, data1_node *n);
123 static void zebraExplain_initAccessInfo(ZebraExplainInfo zei, data1_node *n);
124
125 static data1_node *read_sgml_rec(data1_handle dh, NMEM nmem, Record rec)
126 {
127     return data1_read_sgml(dh, nmem, rec->info[recInfo_storeData]);
128 }
129
130 static void zebraExplain_writeDatabase(ZebraExplainInfo zei,
131                                         struct zebDatabaseInfoB *zdi,
132                                         int key_flush);
133 static void zebraExplain_writeAttributeDetails(ZebraExplainInfo zei,
134                                                 zebAttributeDetails zad,
135                                                 const char *databaseName,
136                                                 int key_flush);
137 static void zebraExplain_writeTarget(ZebraExplainInfo zei, int key_flush);
138 static void zebraExplain_writeAttributeSet(ZebraExplainInfo zei,
139                                             zebAccessObject o,
140                                             int key_flush);
141 static void zebraExplain_writeCategoryList(ZebraExplainInfo zei,
142                                             struct zebraCategoryListInfo *zcl,
143                                             int key_flush);
144
145
146 static Record createRecord(Records records, zint *sysno)
147 {
148     Record rec;
149     if (*sysno)
150     {
151         rec = rec_get(records, *sysno);
152         if (!rec)
153             return 0;
154         xfree(rec->info[recInfo_storeData]);
155     }
156     else
157     {
158         rec = rec_new(records);
159         if (!rec)
160             return 0;
161         *sysno = rec->sysno;
162         
163         rec->info[recInfo_fileType] =
164             rec_strdup("grs.sgml", &rec->size[recInfo_fileType]);
165         rec->info[recInfo_databaseName] =
166             rec_strdup("IR-Explain-1",
167                         &rec->size[recInfo_databaseName]); 
168     }
169     return rec;
170 }
171
172 void zebraExplain_flush(ZebraExplainInfo zei, void *handle)
173 {
174     if (!zei)
175         return;
176     zei->updateHandle = handle;
177     if (zei->write_flag)
178     {
179         struct zebDatabaseInfoB *zdi;
180         zebAccessObject o;
181
182         /* write each database info record */
183         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
184         {
185             zebraExplain_writeDatabase(zei, zdi, 1);
186             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
187                                                 zdi->databaseName, 1);
188         }
189         zebraExplain_writeTarget(zei, 1);
190         zebraExplain_writeCategoryList(zei,
191                                         zei->categoryList,
192                                         1);
193         assert(zei->accessInfo);
194         for (o = zei->accessInfo->attributeSetIds; o; o = o->next)
195             if (!o->sysno)
196                 zebraExplain_writeAttributeSet(zei, o, 1);
197         for (o = zei->accessInfo->schemas; o; o = o->next)
198             if (!o->sysno)
199             {
200 /*              zebraExplain_writeSchema(zei, o, 1); */
201             }
202
203         for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
204         {
205             zebraExplain_writeDatabase(zei, zdi, 0);
206             zebraExplain_writeAttributeDetails(zei, zdi->attributeDetails,
207                                                 zdi->databaseName, 0);
208         }
209         zebraExplain_writeTarget(zei, 0);
210     }
211 }
212
213 void zebraExplain_close(ZebraExplainInfo zei)
214 {
215 #if ZINFO_DEBUG
216     yaz_log(YLOG_LOG, "zebraExplain_close");
217 #endif
218     if (!zei)
219         return;
220     zebraExplain_flush(zei, zei->updateHandle);
221     nmem_destroy(zei->nmem);
222 }
223
224 void zebraExplain_mergeOids (ZebraExplainInfo zei, data1_node *n,
225                              zebAccessObject *op)
226 {
227     data1_node *np;
228
229     for (np = n->child; np; np = np->next)
230     {
231         char str[64];
232         int len;
233         Odr_oid *oid;
234         zebAccessObject ao;
235
236         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "oid"))
237             continue;
238         len = np->child->u.data.len;
239         if (len > 63)
240             len = 63;
241         memcpy(str, np->child->u.data.data, len);
242         str[len] = '\0';
243         
244         oid = odr_getoidbystr_nmem(zei->nmem, str);
245
246         for (ao = *op; ao; ao = ao->next)
247             if (!oid_oidcmp(oid, ao->oid))
248             {
249                 ao->sysno = 1;
250                 break;
251             }
252         if (!ao)
253         {
254             ao = (zebAccessObject) nmem_malloc(zei->nmem, sizeof(*ao));
255             ao->handle = NULL;
256             ao->sysno = 1;
257             ao->oid = oid;
258             ao->next = *op;
259             *op = ao;
260         }
261     }
262 }
263
264 void zebraExplain_mergeAccessInfo(ZebraExplainInfo zei, data1_node *n,
265                                    zebAccessInfo *accessInfo)
266 {
267     data1_node *np;
268     
269     if (!n)
270     {
271         *accessInfo = (zebAccessInfo)
272             nmem_malloc(zei->nmem, sizeof(**accessInfo));
273         (*accessInfo)->attributeSetIds = NULL;
274         (*accessInfo)->schemas = NULL;
275     }
276     else
277     {
278         if (!(n = data1_search_tag(zei->dh, n->child, "accessInfo")))
279             return;
280         if ((np = data1_search_tag(zei->dh, n->child, "attributeSetIds")))
281             zebraExplain_mergeOids(zei, np,
282                                     &(*accessInfo)->attributeSetIds);
283         if ((np = data1_search_tag(zei->dh, n->child, "schemas")))
284             zebraExplain_mergeOids(zei, np,
285                                     &(*accessInfo)->schemas);
286     }
287 }
288
289 /* Explain structure
290     root record
291       of type targetInfo
292       and has sysno = 1
293
294     databaseList (list of databases)
295 */
296 /*
297 Example root:
298 explain:
299   targetInfo: TargetInfo
300     name: Zebra
301     namedResultSets: 1
302     multipleDbSearch: 1
303     nicknames:
304       name: Zebra
305     commonInfo:
306       dateAdded: 20030630190601
307       dateChanged: 20030630190601
308       languageCode: EN
309     accessinfo:
310       unitSystems:
311         string: ISO
312       attributeSetIds:
313         oid: 1.2.840.10003.3.2
314         oid: 1.2.840.10003.3.5
315         oid: 1.2.840.10003.3.1
316       schemas:
317         oid: 1.2.840.10003.13.1000.81.2
318         oid: 1.2.840.10003.13.2
319     zebraInfo:
320       version: 1.3.12
321       databaseList:
322         database:
323           name: Default
324           id: 50
325           attributeDetailsId: 51
326         database:
327           name: IR-Explain-1
328           id: 52
329           attributeDetailsId: 53
330       ordinalSU: 38
331       runNumber: 1
332 nextResultSetPosition = 2
333 */
334
335 ZebraExplainInfo zebraExplain_open(
336     Records records, data1_handle dh,
337     Res res,
338     int writeFlag,
339     void *updateHandle,
340     ZebraExplainUpdateFunc *updateFunc)
341 {
342     Record trec;
343     ZebraExplainInfo zei;
344     struct zebDatabaseInfoB **zdip;
345     time_t our_time;
346     struct tm *tm;
347     NMEM nmem = nmem_create();
348
349 #if ZINFO_DEBUG
350     yaz_log(YLOG_LOG, "zebraExplain_open wr=%d", writeFlag);
351 #endif
352     zei = (ZebraExplainInfo) nmem_malloc(nmem, sizeof(*zei));
353     zei->databaseInfo = 0;
354     zei->write_flag = writeFlag;
355     zei->updateHandle = updateHandle;
356     zei->updateFunc = updateFunc;
357     zei->dirty = 0;
358     zei->ordinalDatabase = 1;
359     zei->curDatabaseInfo = NULL;
360     zei->records = records;
361     zei->nmem = nmem;
362     zei->dh = dh;
363     
364     data1_get_absyn (zei->dh, "explain", DATA1_XPATH_INDEXING_DISABLE);
365
366     zei->attsets = NULL;
367     zei->res = res;
368     zei->categoryList = (struct zebraCategoryListInfo *)
369         nmem_malloc(zei->nmem, sizeof(*zei->categoryList));
370     zei->categoryList->sysno = 0;
371     zei->categoryList->dirty = 0;
372     zei->categoryList->data1_categoryList = NULL;
373
374     if ( atoi(res_get_def(res, "notimestamps", "0") )== 0)
375     {
376         time(&our_time);
377         tm = localtime(&our_time);
378         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
379                  tm->tm_year+1900, tm->tm_mon+1,  tm->tm_mday,
380                  tm->tm_hour, tm->tm_min, tm->tm_sec);
381     } else {
382         sprintf(zei->date, "%04d%02d%02d%02d%02d%02d",
383                  0, 0, 0,  0, 0, 0);
384     }
385     zdip = &zei->databaseInfo;
386     trec = rec_get_root(records);      /* get "root" record */
387
388     zei->ordinalSU = 1;
389     zei->runNumber = 0;
390
391     zebraExplain_mergeAccessInfo(zei, 0, &zei->accessInfo);
392     if (trec)    /* targetInfo already exists ... */
393     {
394         data1_node *node_tgtinfo, *node_zebra, *node_list, *np;
395
396         zei->data1_target = read_sgml_rec(zei->dh, zei->nmem, trec);
397 #if 0
398         if (!zei->data1_target || !zei->data1_target->u.root.absyn)
399 #else
400         if (!zei->data1_target)
401 #endif
402         {
403             yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
404             nmem_destroy(zei->nmem);
405             return 0;
406         }
407 #if ZINFO_DEBUG
408         data1_pr_tree(zei->dh, zei->data1_target, stderr);
409 #endif
410         node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
411                                          "/targetInfo");
412         if (!node_tgtinfo)
413         {
414             yaz_log(YLOG_FATAL, "Node node_tgtinfo missing");
415             nmem_destroy(zei->nmem);
416             return 0;
417         }
418         zebraExplain_mergeAccessInfo(zei, node_tgtinfo,
419                                       &zei->accessInfo);
420
421         node_zebra = data1_search_tag(zei->dh, node_tgtinfo->child,
422                                        "zebraInfo");
423         if (!node_zebra)
424         {
425             yaz_log(YLOG_FATAL, "Node node_zebra missing");
426             nmem_destroy(zei->nmem);
427             return 0;
428         }
429         np = 0;
430         if (node_zebra)
431         {
432             node_list = data1_search_tag(zei->dh, node_zebra->child,
433                                           "databaseList");
434             if (node_list)
435                 np = node_list->child;
436         }
437         for(; np; np = np->next)
438         {
439             data1_node *node_name = NULL;
440             data1_node *node_id = NULL;
441             data1_node *node_aid = NULL;
442             data1_node *np2;
443             if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "database"))
444                 continue;
445             for(np2 = np->child; np2; np2 = np2->next)
446             {
447                 if (np2->which != DATA1N_tag)
448                     continue;
449                 if (!strcmp(np2->u.tag.tag, "name"))
450                     node_name = np2->child;
451                 else if (!strcmp(np2->u.tag.tag, "id"))
452                     node_id = np2->child;
453                 else if (!strcmp(np2->u.tag.tag, "attributeDetailsId"))
454                     node_aid = np2->child;
455             }
456             assert(node_id && node_name && node_aid);
457             
458             *zdip =(struct zebDatabaseInfoB *) 
459                 nmem_malloc(zei->nmem, sizeof(**zdip));
460             (*zdip)->readFlag = 1;
461             (*zdip)->dirty = 0;
462             (*zdip)->data1_database = NULL;
463             (*zdip)->recordCount = 0;
464             (*zdip)->recordBytes = 0;
465             zebraExplain_mergeAccessInfo (zei, 0, &(*zdip)->accessInfo);
466
467             (*zdip)->databaseName = (char *)
468                 nmem_malloc (zei->nmem, 1+node_name->u.data.len);
469             memcpy((*zdip)->databaseName, node_name->u.data.data,
470                    node_name->u.data.len);
471             (*zdip)->databaseName[node_name->u.data.len] = '\0';
472             (*zdip)->sysno = atoi_zn (node_id->u.data.data,
473                                       node_id->u.data.len);
474             (*zdip)->attributeDetails = (zebAttributeDetails)
475                 nmem_malloc (zei->nmem, sizeof(*(*zdip)->attributeDetails));
476             (*zdip)->attributeDetails->sysno = atoi_zn (node_aid->u.data.data,
477                                                         node_aid->u.data.len);
478             (*zdip)->attributeDetails->readFlag = 1;
479             (*zdip)->attributeDetails->dirty = 0;
480             (*zdip)->attributeDetails->SUInfo = NULL;
481
482             zdip = &(*zdip)->next;
483         }
484         if (node_zebra)
485         {
486             np = data1_search_tag(zei->dh, node_zebra->child,
487                                   "ordinalSU");
488             np = np->child;
489             assert (np && np->which == DATA1N_data);
490             zei->ordinalSU = atoi_n(np->u.data.data, np->u.data.len);
491             
492             np = data1_search_tag(zei->dh, node_zebra->child,
493                                   "ordinalDatabase");
494             np = np->child;
495             assert (np && np->which == DATA1N_data);
496             zei->ordinalDatabase = atoi_n(np->u.data.data, np->u.data.len);
497
498             np = data1_search_tag(zei->dh, node_zebra->child,
499                                    "runNumber");
500             np = np->child;
501             assert (np && np->which == DATA1N_data);
502             zei->runNumber = atoi_zn(np->u.data.data, np->u.data.len);
503             yaz_log(YLOG_DEBUG, "read runnumber=" ZINT_FORMAT, zei->runNumber);
504             *zdip = NULL;
505         }
506         rec_free(&trec);
507     }
508     else  /* create initial targetInfo */
509     {
510         data1_node *node_tgtinfo;
511
512         *zdip = NULL;
513         if (writeFlag)
514         {
515             char *sgml_buf;
516             int sgml_len;
517
518             zei->data1_target =
519                 data1_read_sgml(zei->dh, zei->nmem,
520                                  "<explain><targetInfo>TargetInfo\n"
521                                  "<name>Zebra</>\n"
522                                  "<namedResultSets>1</>\n"
523                                  "<multipleDBSearch>1</>\n"
524                                  "<nicknames><name>Zebra</></>\n"
525                                  "</></>\n" );
526             if (!zei->data1_target)
527             {
528                 yaz_log(YLOG_FATAL, "Explain schema missing. Check profilePath");
529                 nmem_destroy(zei->nmem);
530                 return 0;
531             }
532             node_tgtinfo = data1_search_tag(zei->dh, zei->data1_target,
533                                              "/targetInfo");
534             assert(node_tgtinfo);
535
536             zebraExplain_initCommonInfo(zei, node_tgtinfo);
537             zebraExplain_initAccessInfo(zei, node_tgtinfo);
538
539             /* write now because we want to be sure about the sysno */
540             trec = rec_new(records);
541             if (!trec)
542             {
543                 yaz_log(YLOG_FATAL, "Cannot create root Explain record");
544                 nmem_destroy(zei->nmem);
545                 return 0;
546             }
547             trec->info[recInfo_fileType] =
548                 rec_strdup("grs.sgml", &trec->size[recInfo_fileType]);
549             trec->info[recInfo_databaseName] =
550                 rec_strdup("IR-Explain-1", &trec->size[recInfo_databaseName]);
551             
552             sgml_buf = data1_nodetoidsgml(dh, zei->data1_target, 0, &sgml_len);
553             trec->info[recInfo_storeData] = (char *) xmalloc(sgml_len);
554             memcpy(trec->info[recInfo_storeData], sgml_buf, sgml_len);
555             trec->size[recInfo_storeData] = sgml_len;
556                 
557             rec_put(records, &trec);
558             rec_free(&trec);
559         }
560         
561         zebraExplain_newDatabase(zei, "IR-Explain-1", 0);
562             
563         if (!zei->categoryList->dirty)
564         {
565             struct zebraCategoryListInfo *zcl = zei->categoryList;
566             data1_node *node_cl;
567             
568             zcl->dirty = 1;
569             zcl->data1_categoryList =
570                 data1_read_sgml(zei->dh, zei->nmem,
571                                  "<explain><categoryList>CategoryList\n"
572                                  "</></>\n");
573         
574             if (zcl->data1_categoryList)
575             {
576                 node_cl = data1_search_tag(zei->dh, zcl->data1_categoryList,
577                                             "/categoryList");
578                 assert(node_cl);
579                 zebraExplain_initCommonInfo(zei, node_cl);
580             }
581         }
582     }
583     return zei;
584 }
585
586 static void zebraExplain_readAttributeDetails(ZebraExplainInfo zei,
587                                                zebAttributeDetails zad)
588 {
589     Record rec;
590     struct zebSUInfoB **zsuip = &zad->SUInfo;
591     data1_node *node_adinfo, *node_zebra, *node_list, *np;
592
593     assert (zad->sysno);
594     rec = rec_get(zei->records, zad->sysno);
595
596     zad->data1_tree = read_sgml_rec(zei->dh, zei->nmem, rec);
597
598     node_adinfo = data1_search_tag(zei->dh, zad->data1_tree,
599                                     "/attributeDetails");
600     node_zebra = data1_search_tag(zei->dh, node_adinfo->child,
601                                  "zebraInfo");
602     node_list = data1_search_tag(zei->dh, node_zebra->child,
603                                   "attrlist");
604     for (np = node_list->child; np; np = np->next)
605     {
606         data1_node *node_str = NULL;
607         data1_node *node_ordinal = NULL;
608         data1_node *node_type = NULL;
609         data1_node *node_cat = NULL;
610         data1_node *node_doc_occurrences = NULL;
611         data1_node *node_term_occurrences = NULL;
612         data1_node *np2;
613
614         if (np->which != DATA1N_tag || strcmp(np->u.tag.tag, "attr"))
615             continue;
616         for (np2 = np->child; np2; np2 = np2->next)
617         {
618             if (np2->which != DATA1N_tag || !np2->child ||
619                 np2->child->which != DATA1N_data)
620                 continue;
621             if (!strcmp(np2->u.tag.tag, "str"))
622                 node_str = np2->child;
623             else if (!strcmp(np2->u.tag.tag, "ordinal"))
624                 node_ordinal = np2->child;
625             else if (!strcmp(np2->u.tag.tag, "type"))
626                 node_type = np2->child;
627             else if (!strcmp(np2->u.tag.tag, "cat"))
628                 node_cat = np2->child;
629             else if (!strcmp(np2->u.tag.tag, "dococcurrences"))
630                 node_doc_occurrences = np2->child;
631             else if (!strcmp(np2->u.tag.tag, "termoccurrences"))
632                 node_term_occurrences = np2->child;
633             else
634             {
635                 yaz_log(YLOG_LOG, "Unknown tag '%s' in attributeDetails",
636                         np2->u.tag.tag);
637             }
638         }
639         assert(node_ordinal);
640
641         *zsuip = (struct zebSUInfoB *)
642             nmem_malloc(zei->nmem, sizeof(**zsuip));
643
644         if (node_type && node_type->u.data.len > 0)
645             (*zsuip)->info.index_type =  node_type->u.data.data[0];
646         else
647         {
648             yaz_log(YLOG_WARN, "Missing attribute 'type' in attribute info");
649             (*zsuip)->info.index_type = 'w';
650         }
651         if (node_cat && node_cat->u.data.len > 0)
652         {
653             zinfo_index_category_t cat;
654
655             data1_node *np = node_cat;
656             if (!strncmp(np->u.data.data, "index", np->u.data.len))
657                 cat = zinfo_index_category_index;
658             else if (!strncmp(np->u.data.data, "sort", np->u.data.len))
659                 cat = zinfo_index_category_sort;
660             else if (!strncmp(np->u.data.data, "alwaysmatches", 
661                               np->u.data.len))
662                 cat = zinfo_index_category_alwaysmatches;
663             else if (!strncmp(np->u.data.data, "anchor", 
664                               np->u.data.len))
665                 cat = zinfo_index_category_anchor;
666             else
667             {
668                 yaz_log(YLOG_WARN, "Bad index cateogry '%.*s'",
669                         np->u.data.len, np->u.data.data);
670                 cat = zinfo_index_category_index;
671             }
672             (*zsuip)->info.cat = cat;
673         }
674         else
675             (*zsuip)->info.cat = zinfo_index_category_index;
676
677         if (node_doc_occurrences)
678         {
679             data1_node *np = node_doc_occurrences;
680             (*zsuip)->info.doc_occurrences = atoi_zn(np->u.data.data,
681                                                      np->u.data.len);
682         }
683         if (node_term_occurrences)
684         {
685             data1_node *np = node_term_occurrences;
686             (*zsuip)->info.term_occurrences = atoi_zn(np->u.data.data,
687                                                       np->u.data.len);
688         }
689         if (node_str)
690         {
691             (*zsuip)->info.which = ZEB_SU_STR;
692             
693             (*zsuip)->info.u.str = nmem_strdupn(zei->nmem,
694                                                 node_str->u.data.data,
695                                                 node_str->u.data.len);
696         }
697         else
698         {
699             yaz_log(YLOG_WARN, "Missing set/use/str in attribute info");
700             continue;
701         }
702         (*zsuip)->info.ordinal = atoi_n (node_ordinal->u.data.data,
703                                          node_ordinal->u.data.len);
704         zsuip = &(*zsuip)->next;
705     }
706     *zsuip = NULL;
707     zad->readFlag = 0;
708     rec_free(&rec);
709 }
710
711 static void zebraExplain_readDatabase (ZebraExplainInfo zei,
712                                        struct zebDatabaseInfoB *zdi)
713 {
714     Record rec;
715     data1_node *node_dbinfo, *node_zebra, *np;
716
717     assert (zdi->sysno);
718     rec = rec_get (zei->records, zdi->sysno);
719
720     zdi->data1_database = read_sgml_rec (zei->dh, zei->nmem, rec);
721     
722     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
723                                     "/databaseInfo");
724     assert (node_dbinfo);
725     zebraExplain_mergeAccessInfo (zei, node_dbinfo, &zdi->accessInfo);
726
727     node_zebra = data1_search_tag (zei->dh, node_dbinfo->child,
728                                  "zebraInfo");
729     if (node_zebra
730         && (np = data1_search_tag (zei->dh, node_zebra->child,
731                                    "recordBytes")) 
732         && np->child && np->child->which == DATA1N_data)
733         zdi->recordBytes = atoi_zn (np->child->u.data.data,
734                                     np->child->u.data.len);
735
736     if (node_zebra
737         && (np = data1_search_tag (zei->dh, node_zebra->child,
738                                    "ordinalDatabase")) 
739         && np->child && np->child->which == DATA1N_data)
740         zdi->ordinalDatabase = atoi_n(np->child->u.data.data,
741                                       np->child->u.data.len);
742
743     if ((np = data1_search_tag (zei->dh, node_dbinfo->child,
744                                 "recordCount")) &&
745         (np = data1_search_tag (zei->dh, np->child,
746                                 "recordCountActual")) &&
747         np->child->which == DATA1N_data)
748     {
749         zdi->recordCount = atoi_zn (np->child->u.data.data,
750                                     np->child->u.data.len);
751     }
752     zdi->readFlag = 0;
753     rec_free(&rec);
754 }
755
756 int zebraExplain_removeDatabase(ZebraExplainInfo zei, void *update_handle)
757 {
758     struct zebDatabaseInfoB **zdip = &zei->databaseInfo;
759
760     while (*zdip)
761     {
762         if (*zdip == zei->curDatabaseInfo)
763         {
764             struct zebDatabaseInfoB *zdi = *zdip;
765             Record rec;
766
767             zei->dirty = 1;
768             zei->updateHandle = update_handle;
769
770             if (zdi->attributeDetails)
771             {
772                 /* remove attribute details keys and delete it */
773                 zebAttributeDetails zad = zdi->attributeDetails;
774                 
775                 rec = rec_get(zei->records, zad->sysno);
776                 (*zei->updateFunc)(zei->updateHandle, rec, 0);
777                 rec_free(&rec);
778             }
779             /* remove database record keys and delete it */
780             rec = rec_get (zei->records, zdi->sysno);
781             (*zei->updateFunc)(zei->updateHandle, rec, 0);
782             rec_free(&rec);
783
784             /* remove from list */
785             *zdip = zdi->next;
786
787             /* current database is IR-Explain-1 */
788             return 0;
789         }
790         zdip = &(*zdip)->next;
791     }
792     return -1;
793 }
794
795 int zebraExplain_curDatabase (ZebraExplainInfo zei, const char *database)
796 {
797     struct zebDatabaseInfoB *zdi;
798     const char *database_n = strrchr (database, '/');
799
800     if (database_n)
801         database_n++;
802     else
803         database_n = database;
804     
805     assert (zei);
806     if (zei->curDatabaseInfo &&
807         !STRCASECMP (zei->curDatabaseInfo->databaseName, database))
808         return 0;
809     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
810     {
811         if (!STRCASECMP (zdi->databaseName, database_n))
812             break;
813     }
814     if (!zdi)
815         return -1;
816 #if ZINFO_DEBUG
817     yaz_log(YLOG_LOG, "zebraExplain_curDatabase: %s", database);
818 #endif
819     if (zdi->readFlag)
820     {
821 #if ZINFO_DEBUG
822         yaz_log(YLOG_LOG, "zebraExplain_readDatabase: %s", database);
823 #endif
824         zebraExplain_readDatabase (zei, zdi);
825     }
826     if (zdi->attributeDetails->readFlag)
827     {
828 #if ZINFO_DEBUG
829         yaz_log(YLOG_LOG, "zebraExplain_readAttributeDetails: %s", database);
830 #endif
831         zebraExplain_readAttributeDetails (zei, zdi->attributeDetails);
832     }
833     zei->curDatabaseInfo = zdi;
834     return 0;
835 }
836
837 static void zebraExplain_initCommonInfo (ZebraExplainInfo zei, data1_node *n)
838 {
839     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "commonInfo", 0, n);
840     data1_mk_tag_data_text (zei->dh, c, "dateAdded", zei->date, zei->nmem);
841     data1_mk_tag_data_text (zei->dh, c, "dateChanged", zei->date, zei->nmem);
842     data1_mk_tag_data_text (zei->dh, c, "languageCode", "EN", zei->nmem);
843 }
844
845 static void zebraExplain_updateCommonInfo (ZebraExplainInfo zei, data1_node *n)
846 {
847     data1_node *c = data1_search_tag (zei->dh, n->child, "commonInfo");
848     assert (c);
849     data1_mk_tag_data_text_uni (zei->dh, c, "dateChanged", zei->date,
850                                 zei->nmem);
851 }
852
853 static void zebraExplain_initAccessInfo (ZebraExplainInfo zei, data1_node *n)
854 {
855     data1_node *c = data1_mk_tag (zei->dh, zei->nmem, "accessInfo", 0, n);
856     data1_node *d = data1_mk_tag (zei->dh, zei->nmem, "unitSystems", 0, c);
857     data1_mk_tag_data_text (zei->dh, d, "string", "ISO", zei->nmem);
858 }
859
860 static void zebraExplain_updateAccessInfo (ZebraExplainInfo zei, data1_node *n,
861                                            zebAccessInfo accessInfo)
862 {
863     data1_node *c = data1_search_tag (zei->dh, n->child, "accessInfo");
864     data1_node *d;
865     zebAccessObject p;
866     
867     if (!c)
868     {
869         data1_pr_tree (zei->dh, n, stdout);
870         zebra_exit("zebraExplain_updateAccessInfo");
871     }
872
873     if ((p = accessInfo->attributeSetIds))
874     {
875         d = data1_mk_tag_uni (zei->dh, zei->nmem, "attributeSetIds", c);
876         for (; p; p = p->next)
877             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
878     }
879     if ((p = accessInfo->schemas))
880     {
881         d = data1_mk_tag_uni (zei->dh, zei->nmem, "schemas", c);
882         for (; p; p = p->next)
883             data1_mk_tag_data_oid (zei->dh, d, "oid", p->oid, zei->nmem);
884     }
885 }
886
887 int zebraExplain_newDatabase (ZebraExplainInfo zei, const char *database,
888                               int explain_database)
889 {
890     struct zebDatabaseInfoB *zdi;
891     data1_node *node_dbinfo, *node_adinfo;
892     const char *database_n = strrchr (database, '/');
893
894     if (database_n)
895         database_n++;
896     else
897         database_n = database;
898
899 #if ZINFO_DEBUG
900     yaz_log(YLOG_LOG, "zebraExplain_newDatabase: %s", database);
901 #endif
902     assert (zei);
903     for (zdi = zei->databaseInfo; zdi; zdi=zdi->next)
904     {
905         if (!STRCASECMP (zdi->databaseName, database_n))
906             break;
907     }
908     if (zdi)
909         return -1;
910     /* it's new really. make it */
911     zdi = (struct zebDatabaseInfoB *) nmem_malloc (zei->nmem, sizeof(*zdi));
912     zdi->next = zei->databaseInfo;
913     zei->databaseInfo = zdi;
914     zdi->sysno = 0;
915     zdi->recordCount = 0;
916     zdi->recordBytes = 0;
917     zdi->readFlag = 0;
918     zdi->databaseName = nmem_strdup (zei->nmem, database_n);
919
920     zdi->ordinalDatabase = zei->ordinalDatabase++;
921
922     zebraExplain_mergeAccessInfo (zei, 0, &zdi->accessInfo);
923     
924     assert (zei->dh);
925     assert (zei->nmem);
926
927     zdi->data1_database =
928         data1_read_sgml (zei->dh, zei->nmem, 
929                          "<explain><databaseInfo>DatabaseInfo\n"
930                          "</></>\n");
931     if (!zdi->data1_database)
932         return -2;
933
934     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
935                                     "/databaseInfo");
936     assert (node_dbinfo);
937
938     zebraExplain_initCommonInfo (zei, node_dbinfo);
939     zebraExplain_initAccessInfo (zei, node_dbinfo);
940
941     data1_mk_tag_data_text (zei->dh, node_dbinfo, "name",
942                                database, zei->nmem);
943     
944     if (explain_database)
945         data1_mk_tag_data_text (zei->dh, node_dbinfo, "explainDatabase",
946                                 "", zei->nmem);
947     
948     data1_mk_tag_data_text (zei->dh, node_dbinfo, "userFee",
949                             "0", zei->nmem);
950     
951     data1_mk_tag_data_text (zei->dh, node_dbinfo, "available",
952                             "1", zei->nmem);
953     
954 #if ZINFO_DEBUG
955     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
956 #endif
957     zdi->dirty = 1;
958     zei->dirty = 1;
959     zei->curDatabaseInfo = zdi;
960
961     zdi->attributeDetails = (zebAttributeDetails)
962         nmem_malloc (zei->nmem, sizeof(*zdi->attributeDetails));
963     zdi->attributeDetails->readFlag = 0;
964     zdi->attributeDetails->sysno = 0;
965     zdi->attributeDetails->dirty = 1;
966     zdi->attributeDetails->SUInfo = NULL;
967     zdi->attributeDetails->data1_tree =
968         data1_read_sgml (zei->dh, zei->nmem,
969                          "<explain><attributeDetails>AttributeDetails\n"
970                          "</></>\n");
971
972     node_adinfo = data1_search_tag (zei->dh, zdi->attributeDetails->data1_tree,
973                                     "/attributeDetails");
974     assert (node_adinfo);
975
976     zebraExplain_initCommonInfo (zei, node_adinfo);
977
978     data1_mk_tag_data_text(zei->dh, node_adinfo, "name", database, zei->nmem);
979
980     return 0;
981 }
982
983
984 static void zebraExplain_writeCategoryList (ZebraExplainInfo zei,
985                                             struct zebraCategoryListInfo *zcl,
986                                             int key_flush)
987 {
988     char *sgml_buf;
989     int sgml_len;
990     int i;
991     Record drec;
992     data1_node *node_ci, *node_categoryList;
993     zint sysno = 0;
994     static char *category[] = {
995         "CategoryList",
996         "TargetInfo",
997         "DatabaseInfo",
998         "AttributeDetails",
999         NULL
1000     };
1001
1002     assert (zcl);
1003     if (!zcl->dirty)
1004         return ;
1005     zcl->dirty = 1;
1006     node_categoryList = zcl->data1_categoryList;
1007
1008 #if ZINFO_DEBUG
1009     yaz_log(YLOG_LOG, "zebraExplain_writeCategoryList");
1010 #endif
1011
1012     drec = createRecord (zei->records, &sysno);
1013     if (!drec)
1014         return;
1015     
1016     node_ci = data1_search_tag (zei->dh, node_categoryList,
1017                                 "/categoryList");
1018     assert (node_ci);
1019     node_ci = data1_mk_tag (zei->dh, zei->nmem, "categories", 0 /* attr */,
1020                             node_ci);
1021     assert (node_ci);
1022     
1023     for (i = 0; category[i]; i++)
1024     {
1025         data1_node *node_cat = data1_mk_tag (zei->dh, zei->nmem,  "category",
1026                                              0 /* attr */, node_ci);
1027
1028         data1_mk_tag_data_text (zei->dh, node_cat, "name",
1029                                 category[i], zei->nmem);
1030     }
1031     /* extract *searchable* keys from it. We do this here, because
1032        record count, etc. is affected */
1033     if (key_flush)
1034         (*zei->updateFunc)(zei->updateHandle, drec, node_categoryList);
1035
1036     /* convert to "SGML" and write it */
1037 #if ZINFO_DEBUG
1038     data1_pr_tree (zei->dh, node_categoryList, stderr);
1039 #endif
1040     sgml_buf = data1_nodetoidsgml(zei->dh, node_categoryList, 0, &sgml_len);
1041     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1042     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1043     drec->size[recInfo_storeData] = sgml_len;
1044     
1045     rec_put (zei->records, &drec);
1046 }
1047
1048 static void zebraExplain_writeAttributeDetails (ZebraExplainInfo zei,
1049                                                 zebAttributeDetails zad,
1050                                                 const char *databaseName,
1051                                                 int key_flush)
1052 {
1053     char *sgml_buf;
1054     int sgml_len;
1055     Record drec;
1056     data1_node *node_adinfo, *node_list, *node_zebra;
1057     struct zebSUInfoB *zsui;
1058     
1059     if (!zad->dirty)
1060         return;
1061     
1062     zad->dirty = 0;
1063 #if ZINFO_DEBUG
1064     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeDetails");    
1065     data1_pr_tree(zei->dh, zad->data1_tree, stderr);
1066 #endif
1067
1068     drec = createRecord (zei->records, &zad->sysno);
1069     if (!drec)
1070         return;
1071     assert (zad->data1_tree);
1072
1073     node_adinfo = data1_search_tag (zei->dh, zad->data1_tree,
1074                                    "/attributeDetails");
1075     zebraExplain_updateCommonInfo (zei, node_adinfo);
1076
1077     /* zebra info (private) .. no children yet.. so se don't index zebraInfo */
1078     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1079                                  "zebraInfo", node_adinfo);
1080
1081     /* extract *searchable* keys from it. We do this here, because
1082        record count, etc. is affected */
1083     if (key_flush)
1084         (*zei->updateFunc)(zei->updateHandle, drec, zad->data1_tree);
1085     node_list = data1_mk_tag_uni (zei->dh, zei->nmem,
1086                                  "attrlist", node_zebra);
1087     for (zsui = zad->SUInfo; zsui; zsui = zsui->next)
1088     {
1089         data1_node *node_attr;
1090         char index_type_str[2];
1091         
1092         node_attr = data1_mk_tag (zei->dh, zei->nmem, "attr", 0 /* attr */,
1093                                   node_list);
1094
1095         index_type_str[0] = zsui->info.index_type;
1096         index_type_str[1] = '\0';
1097         data1_mk_tag_data_text (zei->dh, node_attr, "type",
1098                                 index_type_str, zei->nmem);
1099         if (zsui->info.which == ZEB_SU_STR)
1100         {
1101             data1_mk_tag_data_text (zei->dh, node_attr, "str",
1102                                     zsui->info.u.str, zei->nmem);
1103         }
1104         data1_mk_tag_data_int (zei->dh, node_attr, "ordinal",
1105                                zsui->info.ordinal, zei->nmem);
1106
1107         data1_mk_tag_data_zint (zei->dh, node_attr, "dococcurrences",
1108                                 zsui->info.doc_occurrences, zei->nmem);
1109         data1_mk_tag_data_zint (zei->dh, node_attr, "termoccurrences",
1110                                 zsui->info.term_occurrences, zei->nmem);
1111         switch(zsui->info.cat)
1112         {
1113         case zinfo_index_category_index:
1114             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1115                                     "index", zei->nmem); break;
1116         case zinfo_index_category_sort:
1117             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1118                                     "sort", zei->nmem); break;
1119         case zinfo_index_category_alwaysmatches:
1120             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1121                                     "alwaysmatches", zei->nmem); break;
1122         case zinfo_index_category_anchor:
1123             data1_mk_tag_data_text (zei->dh, node_attr, "cat",
1124                                     "anchor", zei->nmem); break;
1125         }
1126     }
1127     /* convert to "SGML" and write it */
1128 #if ZINFO_DEBUG
1129     data1_pr_tree (zei->dh, zad->data1_tree, stderr);
1130 #endif
1131     sgml_buf = data1_nodetoidsgml(zei->dh, zad->data1_tree,
1132                                   0, &sgml_len);
1133     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1134     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1135     drec->size[recInfo_storeData] = sgml_len;
1136     
1137     rec_put (zei->records, &drec);
1138 }
1139
1140 static void zebraExplain_writeDatabase (ZebraExplainInfo zei,
1141                                         struct zebDatabaseInfoB *zdi,
1142                                         int key_flush)
1143 {
1144     char *sgml_buf;
1145     int sgml_len;
1146     Record drec;
1147     data1_node *node_dbinfo, *node_count, *node_zebra;
1148     
1149     if (!zdi->dirty)
1150         return;
1151
1152     zdi->dirty = 0;
1153 #if ZINFO_DEBUG
1154     yaz_log(YLOG_LOG, "zebraExplain_writeDatabase %s", zdi->databaseName);
1155 #endif
1156     drec = createRecord (zei->records, &zdi->sysno);
1157     if (!drec)
1158         return;
1159     assert (zdi->data1_database);
1160
1161     node_dbinfo = data1_search_tag (zei->dh, zdi->data1_database,
1162                                     "/databaseInfo");
1163
1164     assert (node_dbinfo);
1165     zebraExplain_updateCommonInfo (zei, node_dbinfo);
1166     zebraExplain_updateAccessInfo (zei, node_dbinfo, zdi->accessInfo);
1167
1168     /* record count */
1169     node_count = data1_mk_tag_uni (zei->dh, zei->nmem,
1170                                  "recordCount", node_dbinfo);
1171     data1_mk_tag_data_zint (zei->dh, node_count, "recordCountActual",
1172                             zdi->recordCount, zei->nmem);
1173
1174     /* zebra info (private) */
1175     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1176                                  "zebraInfo", node_dbinfo);
1177
1178     /* extract *searchable* keys from it. We do this here, because
1179        record count, etc. is affected */
1180     if (key_flush)
1181         (*zei->updateFunc)(zei->updateHandle, drec, zdi->data1_database);
1182     data1_mk_tag_data_zint (zei->dh, node_zebra,
1183                            "recordBytes", zdi->recordBytes, zei->nmem);
1184
1185     data1_mk_tag_data_zint(zei->dh, node_zebra,
1186                            "ordinalDatabase", zdi->ordinalDatabase, zei->nmem);
1187
1188     /* convert to "SGML" and write it */
1189 #if ZINFO_DEBUG
1190     data1_pr_tree (zei->dh, zdi->data1_database, stderr);
1191 #endif
1192     sgml_buf = data1_nodetoidsgml(zei->dh, zdi->data1_database,
1193                                   0, &sgml_len);
1194     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1195     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1196     drec->size[recInfo_storeData] = sgml_len;
1197     
1198     rec_put (zei->records, &drec);
1199 }
1200
1201 static void writeAttributeValues (ZebraExplainInfo zei,
1202                                   data1_node *node_values,
1203                                   data1_attset *attset)
1204 {
1205     data1_att *atts;
1206     data1_attset_child *c;
1207
1208     if (!attset)
1209         return;
1210
1211     for (c = attset->children; c; c = c->next)
1212         writeAttributeValues (zei, node_values, c->child);
1213     for (atts = attset->atts; atts; atts = atts->next)
1214     {
1215         data1_node *node_value;
1216         
1217         node_value = data1_mk_tag (zei->dh, zei->nmem, "attributeValue",
1218                                    0 /* attr */, node_values);
1219         data1_mk_tag_data_text (zei->dh, node_value, "name",
1220                                 atts->name, zei->nmem);
1221         node_value = data1_mk_tag (zei->dh, zei->nmem, "value",
1222                                    0 /* attr */, node_value);
1223         data1_mk_tag_data_int (zei->dh, node_value, "numeric",
1224                                atts->value, zei->nmem);
1225     }
1226 }
1227
1228
1229 static void zebraExplain_writeAttributeSet (ZebraExplainInfo zei,
1230                                             zebAccessObject o,
1231                                             int key_flush)
1232 {
1233     char *sgml_buf;
1234     int sgml_len;
1235     Record drec;
1236     data1_node *node_root, *node_attinfo, *node_attributes, *node_atttype;
1237     data1_node *node_values;
1238     struct oident *entp;
1239     struct data1_attset *attset = NULL;
1240     
1241     if ((entp = oid_getentbyoid (o->oid)))
1242         attset = data1_attset_search_id (zei->dh, entp->value);
1243             
1244 #if ZINFO_DEBUG
1245     yaz_log(YLOG_LOG, "zebraExplain_writeAttributeSet %s",
1246           attset ? attset->name : "<unknown>");    
1247 #endif
1248
1249     drec = createRecord (zei->records, &o->sysno);
1250     if (!drec)
1251         return;
1252     node_root =
1253         data1_read_sgml (zei->dh, zei->nmem,
1254                          "<explain><attributeSetInfo>AttributeSetInfo\n"
1255                          "</></>\n" );
1256
1257     node_attinfo = data1_search_tag (zei->dh, node_root,
1258                                    "/attributeSetInfo");
1259
1260     assert (node_attinfo);
1261     zebraExplain_initCommonInfo (zei, node_attinfo);
1262     zebraExplain_updateCommonInfo (zei, node_attinfo);
1263
1264     data1_mk_tag_data_oid (zei->dh, node_attinfo,
1265                             "oid", o->oid, zei->nmem);
1266     if (attset && attset->name)
1267         data1_mk_tag_data_text (zei->dh, node_attinfo,
1268                                 "name", attset->name, zei->nmem);
1269     
1270     node_attributes = data1_mk_tag_uni (zei->dh, zei->nmem,
1271                                       "attributes", node_attinfo);
1272     node_atttype = data1_mk_tag_uni (zei->dh, zei->nmem,
1273                                    "attributeType", node_attributes);
1274     data1_mk_tag_data_text (zei->dh, node_atttype,
1275                             "name", "Use", zei->nmem);
1276     data1_mk_tag_data_text (zei->dh, node_atttype,
1277                             "description", "Use Attribute", zei->nmem);
1278     data1_mk_tag_data_int (zei->dh, node_atttype,
1279                            "type", 1, zei->nmem);
1280     node_values = data1_mk_tag (zei->dh, zei->nmem,
1281                                 "attributeValues", 0 /* attr */, node_atttype);
1282     if (attset)
1283         writeAttributeValues (zei, node_values, attset);
1284
1285     /* extract *searchable* keys from it. We do this here, because
1286        record count, etc. is affected */
1287     if (key_flush)
1288         (*zei->updateFunc)(zei->updateHandle, drec, node_root);
1289     /* convert to "SGML" and write it */
1290 #if ZINFO_DEBUG
1291     data1_pr_tree (zei->dh, node_root, stderr);
1292 #endif
1293     sgml_buf = data1_nodetoidsgml(zei->dh, node_root, 0, &sgml_len);
1294     drec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1295     memcpy (drec->info[recInfo_storeData], sgml_buf, sgml_len);
1296     drec->size[recInfo_storeData] = sgml_len;
1297     
1298     rec_put (zei->records, &drec);
1299 }
1300
1301 static void zebraExplain_writeTarget (ZebraExplainInfo zei, int key_flush)
1302 {
1303     struct zebDatabaseInfoB *zdi;
1304     data1_node *node_tgtinfo, *node_list, *node_zebra;
1305     Record trec;
1306     int sgml_len;
1307     char *sgml_buf;
1308
1309     if (!zei->dirty)
1310         return;
1311     zei->dirty = 0;
1312
1313     trec = rec_get_root(zei->records);
1314     xfree (trec->info[recInfo_storeData]);
1315
1316     node_tgtinfo = data1_search_tag (zei->dh, zei->data1_target,
1317                                      "/targetInfo");
1318     assert (node_tgtinfo);
1319
1320     zebraExplain_updateCommonInfo (zei, node_tgtinfo);
1321     zebraExplain_updateAccessInfo (zei, node_tgtinfo, zei->accessInfo);
1322
1323     node_zebra = data1_mk_tag_uni (zei->dh, zei->nmem,
1324                                  "zebraInfo", node_tgtinfo);
1325     /* convert to "SGML" and write it */
1326     if (key_flush)
1327         (*zei->updateFunc)(zei->updateHandle, trec, zei->data1_target);
1328
1329     data1_mk_tag_data_text (zei->dh, node_zebra, "version",
1330                                ZEBRAVER, zei->nmem);
1331     node_list = data1_mk_tag (zei->dh, zei->nmem,
1332                               "databaseList", 0 /* attr */, node_zebra);
1333     for (zdi = zei->databaseInfo; zdi; zdi = zdi->next)
1334     {
1335         data1_node *node_db;
1336         node_db = data1_mk_tag (zei->dh, zei->nmem,
1337                                 "database", 0 /* attr */, node_list);
1338         data1_mk_tag_data_text (zei->dh, node_db, "name",
1339                                 zdi->databaseName, zei->nmem);
1340         data1_mk_tag_data_zint (zei->dh, node_db, "id",
1341                                 zdi->sysno, zei->nmem);
1342         data1_mk_tag_data_zint (zei->dh, node_db, "attributeDetailsId",
1343                                 zdi->attributeDetails->sysno, zei->nmem);
1344     }
1345     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalSU",
1346                            zei->ordinalSU, zei->nmem);
1347
1348     data1_mk_tag_data_int (zei->dh, node_zebra, "ordinalDatabase",
1349                            zei->ordinalDatabase, zei->nmem);
1350
1351     data1_mk_tag_data_zint (zei->dh, node_zebra, "runNumber",
1352                             zei->runNumber, zei->nmem);
1353
1354 #if ZINFO_DEBUG
1355     data1_pr_tree (zei->dh, zei->data1_target, stderr);
1356 #endif
1357     sgml_buf = data1_nodetoidsgml(zei->dh, zei->data1_target,
1358                                   0, &sgml_len);
1359     trec->info[recInfo_storeData] = (char *) xmalloc (sgml_len);
1360     memcpy (trec->info[recInfo_storeData], sgml_buf, sgml_len);
1361     trec->size[recInfo_storeData] = sgml_len;
1362     
1363     rec_put (zei->records, &trec);
1364 }
1365
1366 int zebraExplain_lookup_attr_str(ZebraExplainInfo zei, 
1367                                  zinfo_index_category_t cat,
1368                                  int index_type,
1369                                  const char *str)
1370 {
1371     struct zebSUInfoB **zsui;
1372
1373     assert (zei->curDatabaseInfo);
1374     for (zsui = &zei->curDatabaseInfo->attributeDetails->SUInfo;
1375          *zsui; zsui = &(*zsui)->next)
1376         if ( (index_type == -1 || (*zsui)->info.index_type == index_type)
1377              && (*zsui)->info.cat == cat
1378              && (*zsui)->info.which == ZEB_SU_STR 
1379              && !yaz_matchstr((*zsui)->info.u.str, str))
1380         {
1381             struct zebSUInfoB *zsui_this = *zsui;
1382
1383             /* take it out of the list and move to front */
1384             *zsui = (*zsui)->next;
1385             zsui_this->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1386             zei->curDatabaseInfo->attributeDetails->SUInfo = zsui_this;
1387
1388             return zsui_this->info.ordinal;
1389         }
1390     return -1;
1391 }
1392
1393 int zebraExplain_trav_ord(ZebraExplainInfo zei, void *handle,
1394                           int (*f)(void *handle, int ord))
1395 {
1396     struct zebDatabaseInfoB *zdb = zei->curDatabaseInfo;
1397     if (zdb)
1398     {
1399         struct zebSUInfoB *zsui = zdb->attributeDetails->SUInfo;
1400         for ( ;zsui; zsui = zsui->next)
1401             (*f)(handle,  zsui->info.ordinal);
1402     }
1403     return 0;
1404 }
1405
1406
1407 struct zebSUInfoB *zebraExplain_get_sui_info (ZebraExplainInfo zei, int ord,
1408                                               int dirty_mark,
1409                                               const char **db)
1410 {
1411     struct zebDatabaseInfoB *zdb;
1412
1413     for (zdb = zei->databaseInfo; zdb; zdb = zdb->next)
1414     {
1415         struct zebSUInfoB **zsui;
1416
1417         if (zdb->attributeDetails->readFlag)
1418             zebraExplain_readAttributeDetails (zei, zdb->attributeDetails);
1419
1420         for (zsui = &zdb->attributeDetails->SUInfo; *zsui;
1421              zsui = &(*zsui)->next)
1422             if ((*zsui)->info.ordinal == ord)
1423             {
1424                 struct zebSUInfoB *zsui_this = *zsui;
1425                 
1426                 /* take it out of the list and move to front */
1427                 *zsui = (*zsui)->next;
1428                 zsui_this->next = zdb->attributeDetails->SUInfo;
1429                 zdb->attributeDetails->SUInfo = zsui_this;
1430
1431                 if (dirty_mark)
1432                     zdb->attributeDetails->dirty = 1;
1433                 if (db)
1434                     *db = zdb->databaseName;
1435                 return zsui_this;
1436             }
1437     }
1438     return 0;
1439 }
1440
1441
1442
1443 int zebraExplain_ord_adjust_occurrences(ZebraExplainInfo zei, int ord,
1444                                         int term_delta, int doc_delta)
1445 {
1446     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 1, 0);
1447     if (zsui)
1448     {
1449         zsui->info.term_occurrences += term_delta;
1450         zsui->info.doc_occurrences += doc_delta;
1451         return 0;
1452     }
1453     return -1;
1454 }
1455
1456 int zebraExplain_ord_get_occurrences(ZebraExplainInfo zei, int ord,
1457                                      zint *term_occurrences,
1458                                      zint *doc_occurrences)
1459 {
1460     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1461     if (zsui)
1462     {
1463         *term_occurrences = zsui->info.term_occurrences;
1464         *doc_occurrences = zsui->info.doc_occurrences;
1465         return 0;
1466     }
1467     return -1;
1468 }
1469
1470 zint zebraExplain_ord_get_doc_occurrences(ZebraExplainInfo zei, int ord)
1471 {
1472     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1473     if (zsui)
1474         return zsui->info.doc_occurrences;
1475     return 0;
1476 }
1477
1478 zint zebraExplain_ord_get_term_occurrences(ZebraExplainInfo zei, int ord)
1479 {
1480     struct zebSUInfoB *zsui = zebraExplain_get_sui_info(zei, ord, 0, 0);
1481     if (zsui)
1482         return zsui->info.term_occurrences;
1483     return 0;
1484 }
1485
1486 int zebraExplain_lookup_ord(ZebraExplainInfo zei, int ord,
1487                             int *index_type, 
1488                             const char **db,
1489                             const char **string_index)
1490 {
1491     struct zebSUInfoB *zsui;
1492
1493     if (index_type)
1494         *index_type = 0;
1495     if (string_index)
1496         *string_index = 0;
1497
1498     zsui = zebraExplain_get_sui_info(zei, ord, 0, db);
1499     if (zsui)
1500     {
1501         if (zsui->info.which == ZEB_SU_STR)
1502             if (string_index)
1503                 *string_index = zsui->info.u.str;
1504         if (index_type)
1505             *index_type = zsui->info.index_type;
1506         return 0;
1507     }
1508     return -1;
1509 }
1510
1511
1512
1513 zebAccessObject zebraExplain_announceOid (ZebraExplainInfo zei,
1514                                           zebAccessObject *op,
1515                                           Odr_oid *oid)
1516 {
1517     zebAccessObject ao;
1518     
1519     for (ao = *op; ao; ao = ao->next)
1520         if (!oid_oidcmp (oid, ao->oid))
1521             break;
1522     if (!ao)
1523     {
1524         ao = (zebAccessObject) nmem_malloc (zei->nmem, sizeof(*ao));
1525         ao->handle = NULL;
1526         ao->sysno = 0;
1527         ao->oid = odr_oiddup_nmem (zei->nmem, oid);
1528         ao->next = *op;
1529         *op = ao;
1530     }
1531     return ao;
1532 }
1533
1534 void zebraExplain_addAttributeSet (ZebraExplainInfo zei, int set)
1535 {
1536     oident oe;
1537     int oid[OID_SIZE];
1538
1539     oe.proto = PROTO_Z3950;
1540     oe.oclass = CLASS_ATTSET;
1541     oe.value = (enum oid_value) set;
1542
1543     if (oid_ent_to_oid (&oe, oid))
1544     {
1545         zebraExplain_announceOid (zei, &zei->accessInfo->attributeSetIds, oid);
1546         zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1547                                   accessInfo->attributeSetIds, oid);
1548     }
1549 }
1550
1551 struct zebSUInfoB *zebraExplain_add_sui_info(ZebraExplainInfo zei,
1552                                              zinfo_index_category_t cat,
1553                                              int index_type)
1554 {
1555     struct zebSUInfoB *zsui;
1556
1557     assert (zei->curDatabaseInfo);
1558     zsui = (struct zebSUInfoB *) nmem_malloc (zei->nmem, sizeof(*zsui));
1559     zsui->next = zei->curDatabaseInfo->attributeDetails->SUInfo;
1560     zei->curDatabaseInfo->attributeDetails->SUInfo = zsui;
1561     zei->curDatabaseInfo->attributeDetails->dirty = 1;
1562     zei->dirty = 1;
1563     zsui->info.index_type = index_type;
1564     zsui->info.cat = cat;
1565     zsui->info.doc_occurrences = 0;
1566     zsui->info.term_occurrences = 0;
1567     zsui->info.ordinal = (zei->ordinalSU)++;
1568     return zsui;
1569 }
1570
1571 int zebraExplain_add_attr_str(ZebraExplainInfo zei, 
1572                               zinfo_index_category_t cat,
1573                               int index_type,
1574                               const char *index_name)
1575 {
1576     struct zebSUInfoB *zsui = zebraExplain_add_sui_info(zei, cat, index_type);
1577
1578     zsui->info.which = ZEB_SU_STR;
1579     zsui->info.u.str = nmem_strdup(zei->nmem, index_name);
1580     return zsui->info.ordinal;
1581 }
1582
1583 void zebraExplain_addSchema (ZebraExplainInfo zei, Odr_oid *oid)
1584 {
1585     zebraExplain_announceOid (zei, &zei->accessInfo->schemas, oid);
1586     zebraExplain_announceOid (zei, &zei->curDatabaseInfo->
1587                               accessInfo->schemas, oid);
1588 }
1589
1590 void zebraExplain_recordBytesIncrement (ZebraExplainInfo zei, int adjust_num)
1591 {
1592     assert (zei->curDatabaseInfo);
1593
1594     if (adjust_num)
1595     {
1596         zei->curDatabaseInfo->recordBytes += adjust_num;
1597         zei->curDatabaseInfo->dirty = 1;
1598     }
1599 }
1600
1601 void zebraExplain_recordCountIncrement (ZebraExplainInfo zei, int adjust_num)
1602 {
1603     assert (zei->curDatabaseInfo);
1604
1605     if (adjust_num)
1606     {
1607         zei->curDatabaseInfo->recordCount += adjust_num;
1608         zei->curDatabaseInfo->dirty = 1;
1609     }
1610 }
1611
1612 zint zebraExplain_runNumberIncrement (ZebraExplainInfo zei, int adjust_num)
1613 {
1614     if (adjust_num)
1615     {
1616         zei->dirty = 1;
1617     }
1618     return zei->runNumber += adjust_num;
1619 }
1620
1621 RecordAttr *rec_init_attr (ZebraExplainInfo zei, Record rec)
1622 {
1623     RecordAttr *recordAttr;
1624
1625     if (rec->info[recInfo_attr])
1626         return (RecordAttr *) rec->info[recInfo_attr];
1627     recordAttr = (RecordAttr *) xmalloc (sizeof(*recordAttr));
1628
1629     memset(recordAttr, '\0', sizeof(*recordAttr));
1630     rec->info[recInfo_attr] = (char *) recordAttr;
1631     rec->size[recInfo_attr] = sizeof(*recordAttr);
1632     
1633     recordAttr->recordSize = 0;
1634     recordAttr->recordOffset = 0;
1635     recordAttr->runNumber = zei->runNumber;
1636     recordAttr->staticrank = 0;
1637     return recordAttr;
1638 }
1639
1640 static void att_loadset(void *p, const char *n, const char *name)
1641 {
1642     data1_handle dh = (data1_handle) p;
1643     if (!data1_get_attset (dh, name))
1644         yaz_log(YLOG_WARN, "Directive attset failed for %s", name);
1645 }
1646
1647 int zebraExplain_get_database_ord(ZebraExplainInfo zei)
1648 {
1649     if (!zei->curDatabaseInfo)
1650         return -1;
1651     return zei->curDatabaseInfo->ordinalDatabase;
1652 }
1653
1654 void zebraExplain_loadAttsets (data1_handle dh, Res res)
1655 {
1656     res_trav(res, "attset", dh, att_loadset);
1657 }
1658
1659 /*
1660      zebraExplain_addSU adds to AttributeDetails for a database and
1661      adds attributeSet (in AccessInfo area) to DatabaseInfo if it doesn't
1662      exist for the database.
1663
1664      If the database doesn't exist globally (in TargetInfo) an 
1665      AttributeSetInfo must be added (globally).
1666  */
1667 /*
1668  * Local variables:
1669  * c-basic-offset: 4
1670  * indent-tabs-mode: nil
1671  * End:
1672  * vim: shiftwidth=4 tabstop=8 expandtab
1673  */
1674