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